From: Ash Berlin Date: Tue, 15 Jan 2008 23:34:54 +0000 (+0000) Subject: Add renamed_from to tables. X-Git-Tag: v0.11008~348^2~4 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=46bf5655a6ca0e64ddae0e920d6adc744d798f3c;p=dbsrgits%2FSQL-Translator.git Add renamed_from to tables. --- diff --git a/META.yml b/META.yml index 32fa024..48dcb35 100644 --- a/META.yml +++ b/META.yml @@ -222,7 +222,6 @@ provides: version: 1.08 SQL::Translator::Schema::Table: file: lib/SQL/Translator/Schema/Table.pm - version: 1.37 SQL::Translator::Schema::Trigger: file: lib/SQL/Translator/Schema/Trigger.pm version: 1.09 diff --git a/lib/SQL/Translator/Diff.pm b/lib/SQL/Translator/Diff.pm index 9692e1c..7ee9fc1 100644 --- a/lib/SQL/Translator/Diff.pm +++ b/lib/SQL/Translator/Diff.pm @@ -32,6 +32,7 @@ my @diff_hash_keys = qw/ fields_to_rename fields_to_drop table_options + table_renamed_from /; __PACKAGE__->mk_accessors(@diff_arrays, 'table_diff_hash'); @@ -82,11 +83,29 @@ sub compute_differences { $producer_class->$preprocess($target_schema); } + my %src_tables_checked = (); my @tar_tables = sort { $a->name cmp $b->name } $target_schema->get_tables; ## do original/source tables exist in target? for my $tar_table ( @tar_tables ) { my $tar_table_name = $tar_table->name; - my $src_table = $source_schema->get_table( $tar_table_name, $self->case_insensitive ); + + my $src_table; + + $self->table_diff_hash->{$tar_table_name} = { + map {$_ => [] } @diff_hash_keys + }; + + if (my $old_name = $tar_table->extra('renamed_from')) { + $src_table = $source_schema->get_table( $old_name, $self->case_insensitive ); + if ($src_table) { + $self->table_diff_hash->{$tar_table_name}{table_renamed_from} = [ [$src_table, $tar_table] ]; + } else { + delete $tar_table->extra->{renamed_from}; + warn qq#Renamed table can't find old table "$old_name" for renamed table\n#; + } + } else { + $src_table = $source_schema->get_table( $tar_table_name, $self->case_insensitive ); + } unless ( $src_table ) { ## table is new @@ -95,9 +114,10 @@ sub compute_differences { next; } - $self->table_diff_hash->{$tar_table_name} = { - map {$_ => [] } @diff_hash_keys - }; + my $src_table_name = $src_table->name; + $src_table_name = lc $src_table_name if $self->case_insensitive; + $src_tables_checked{$src_table_name} = 1; + $self->diff_table_options($src_table, $tar_table); @@ -111,16 +131,11 @@ sub compute_differences { for my $src_table ( $source_schema->get_tables ) { my $src_table_name = $src_table->name; - my $tar_table = $target_schema->get_table( $src_table_name, $self->case_insensitive ); - unless ( $tar_table ) { - $self->table_diff_hash->{$src_table_name} = { - map {$_ => [] } @diff_hash_keys - }; + $src_table_name = lc $src_table_name if $self->case_insensitive; - push @{ $self->tables_to_drop}, $src_table; - next; - } + push @{ $self->tables_to_drop}, $src_table + unless $src_tables_checked{$src_table_name}; } return $self; @@ -148,7 +163,8 @@ sub produce_diff_sql { fields_to_alter => 'alter_field', fields_to_rename => 'rename_field', fields_to_drop => 'drop_field', - table_options => 'alter_table' + table_options => 'alter_table', + table_renamed_from => 'rename_table', ); my @diffs; @@ -169,6 +185,7 @@ sub produce_diff_sql { } } else { + # If we have any table renames we need to do those first; my %flattened_diffs; foreach my $table ( sort keys %{$self->table_diff_hash} ) { my $table_diff = $self->table_diff_hash->{$table}; @@ -178,7 +195,7 @@ sub produce_diff_sql { } push @diffs, map( { - if (@{$flattened_diffs{$_}}) { + if (@{ $flattened_diffs{$_} || [] }) { my $meth = $producer_class->can($_); $meth ? map { my $sql = $meth->(ref $_ eq 'ARRAY' ? @$_ : $_); $sql ? ("$sql;") : () } @{ $flattened_diffs{$_} } @@ -187,7 +204,8 @@ sub produce_diff_sql { : die "$producer_class cant $_"; } else { () } - } qw/alter_drop_constraint + } qw/rename_table + alter_drop_constraint alter_drop_index drop_field add_field @@ -213,7 +231,7 @@ sub produce_diff_sql { unshift @diffs, # Remove begin/commit here, since we wrap everything in one. - grep { $_ !~ /^(?:COMMIT|BEGIN(?: TRANSACTION)?);/ } $producer_class->can('produce')->($translator); + grep { $_ !~ /^(?:COMMIT|START(?: TRANSACTION)?|BEGIN(?: TRANSACTION)?);/ } $producer_class->can('produce')->($translator); } if (my @tables_to_drop = @{ $self->{tables_to_drop} || []} ) { @@ -275,6 +293,10 @@ sub diff_table_constraints { CONSTRAINT_CREATE: for my $c_tar ( $tar_table->get_constraints ) { for my $c_src ( $src_table->get_constraints ) { + + # This is a bit of a hack - needed for renaming tables to work + local $c_src->{table} = $tar_table; + if ( $c_tar->equals($c_src, $self->case_insensitive, $self->ignore_constraint_names) ) { $checked_constraints{$c_src} = 1; next CONSTRAINT_CREATE; @@ -286,6 +308,10 @@ sub diff_table_constraints { CONSTRAINT_DROP: for my $c_src ( $src_table->get_constraints ) { + + # This is a bit of a hack - needed for renaming tables to work + local $c_src->{table} = $tar_table; + next if !$self->ignore_constraint_names && $checked_constraints{$c_src}; for my $c_tar ( $tar_table->get_constraints ) { next CONSTRAINT_DROP if $c_src->equals($c_tar, $self->case_insensitive, $self->ignore_constraint_names); @@ -307,10 +333,14 @@ sub diff_table_fields { if (my $old_name = $tar_table_field->extra->{renamed_from}) { my $src_table_field = $src_table->get_field( $old_name, $self->case_insensitive ); - die qq#Renamed cant find "@{[$src_table->name]}.$old_name" for renamed column\n# unless $src_table_field; - push @{$self->table_diff_hash->{$tar_table}{fields_to_rename} }, [ $src_table_field, $tar_table_field ]; - $renamed_source_fields{$old_name} = 1; - next; + unless ($src_table_field) { + warn qq#Renamed column can't find old column "@{[$src_table->name]}.$old_name" for renamed column\n#; + delete $tar_table_field->extra->{renamed_from}; + } else { + push @{$self->table_diff_hash->{$tar_table}{fields_to_rename} }, [ $src_table_field, $tar_table_field ]; + $renamed_source_fields{$old_name} = 1; + next; + } } my $src_table_field = $src_table->get_field( $f_tar_name, $self->case_insensitive ); @@ -448,8 +478,11 @@ thrown. =item * C +=item * C (optional) + =item * C (optional) + =back If the producer supports C, it will be called with the diff --git a/lib/SQL/Translator/Producer/MySQL.pm b/lib/SQL/Translator/Producer/MySQL.pm index 1252f89..a1f0c37 100644 --- a/lib/SQL/Translator/Producer/MySQL.pm +++ b/lib/SQL/Translator/Producer/MySQL.pm @@ -665,7 +665,8 @@ sub batch_alter_table { my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_"; map { $meth->(ref $_ eq 'ARRAY' ? @$_ : $_) } @{ $diff_hash->{$_} } } else { () } - } qw/alter_drop_constraint + } qw/rename_table + alter_drop_constraint alter_drop_index drop_field add_field @@ -675,6 +676,11 @@ sub batch_alter_table { alter_create_constraint alter_table/; + # rename_table makes things a bit more complex + my $renamed_from = ""; + $renamed_from = $diff_hash->{rename_table}[0][0]->name + if $diff_hash->{rename_table} && @{$diff_hash->{rename_table}}; + return unless @stmts; # Just zero or one stmts. return now return "@stmts;" unless @stmts > 1; @@ -682,24 +688,36 @@ sub batch_alter_table { # Now strip off the 'ALTER TABLE xyz' of all but the first one my $qt = $options->{quote_table_name} || ''; - my $table_name = $qt . $table->name . $qt; + my $table_name = $qt . $renamed_from || $table->name . $qt; my $first = shift @stmts; my ($alter_table) = $first =~ /^(ALTER TABLE \Q$table_name\E )/; + my $re = qr/^$alter_table/; + $re = qr/^ALTER TABLE \Q$qt@{[$table->name]}$qt\E / if $renamed_from; my $padd = " " x length($alter_table); return join( ",\n", $first, map { s/$re//; $padd . $_ } @stmts) . ';'; } sub drop_table { - my ($table) = @_; + my ($table, $options) = @_; + + my $qt = $options->{quote_table_names} || ''; # Drop (foreign key) constraints so table drops cleanly - my @sql = batch_alter_table($table, { alter_drop_constraint => [ grep { $_->type eq 'FOREIGN KEY' } $table->get_constraints ] }); + my @sql = batch_alter_table($table, { alter_drop_constraint => [ grep { $_->type eq 'FOREIGN KEY' } $table->get_constraints ] }, $options); + + return join("\n", @sql, "DROP TABLE $qt$table$qt;"); + +} + +sub rename_table { + my ($old_table, $new_table, $options) = @_; - return join("\n", @sql, "DROP TABLE $table;"); + my $qt = $options->{quote_table_names} || ''; + return "ALTER TABLE $qt$old_table$qt RENAME TO $qt$new_table$qt"; } sub next_unused_name { diff --git a/lib/SQL/Translator/Producer/SQLite.pm b/lib/SQL/Translator/Producer/SQLite.pm index dca2aea..149ba7b 100644 --- a/lib/SQL/Translator/Producer/SQLite.pm +++ b/lib/SQL/Translator/Producer/SQLite.pm @@ -344,8 +344,6 @@ sub alter_drop_index { sub batch_alter_table { my ($table, $diffs) = @_; - $DB::single = 1 if $table->name eq 'deleted'; - # If we have any of the following # # rename_field @@ -364,32 +362,49 @@ sub batch_alter_table { # COMMIT; # # Fun, eh? + # + # If we have rename_field we do similarly. + + my $table_name = $table->name; + my $renaming = $diffs->{rename_table} && @{$diffs->{rename_table}}; if ( @{$diffs->{rename_field}} == 0 && @{$diffs->{alter_field}} == 0 && - @{$diffs->{drop_field}} == 0) { + @{$diffs->{drop_field}} == 0 + ) { return join("\n", map { my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_"; map { my $sql = $meth->(ref $_ eq 'ARRAY' ? @$_ : $_); $sql ? ("$sql;") : () } @{ $diffs->{$_} } - } grep { @{$diffs->{$_}} } keys %$diffs); + } grep { @{$diffs->{$_}} } + qw/rename_table + alter_drop_constraint + alter_drop_index + drop_field + add_field + alter_field + rename_field + alter_create_index + alter_create_constraint + alter_table/); } my @sql; + my $old_table = $renaming ? $diffs->{rename_table}[0][0] : $table; do { - local $table->{name} = $table->name . '_temp_alter'; + local $table->{name} = $table_name . '_temp_alter'; # We only want the table - dont care about indexes on tmp table my ($table_sql) = create_table($table, {no_comments => 1, temporary_table => 1}); push @sql,$table_sql; }; - push @sql, "INSERT INTO @{[$table]}_temp_alter SELECT @{[ join(', ', $table->get_fields)]} FROM @{[$table]}", - "DROP TABLE @{[$table]}", + push @sql, "INSERT INTO @{[$table_name]}_temp_alter SELECT @{[ join(', ', $old_table->get_fields)]} FROM @{[$old_table]}", + "DROP TABLE @{[$old_table]}", create_table($table, { no_comments => 1 }), - "INSERT INTO @{[$table]} SELECT @{[ join(', ', $table->get_fields)]} FROM @{[$table]}_temp_alter", - "DROP TABLE @{[$table]}_temp_alter"; + "INSERT INTO @{[$table_name]} SELECT @{[ join(', ', $old_table->get_fields)]} FROM @{[$table_name]}_temp_alter", + "DROP TABLE @{[$table_name]}_temp_alter"; return join(";\n", @sql, ""); } @@ -399,6 +414,15 @@ sub drop_table { return "DROP TABLE $table;"; } +sub rename_table { + my ($old_table, $new_table, $options) = @_; + + my $qt = $options->{quote_table_names} || ''; + + return "ALTER TABLE $qt$old_table$qt RENAME TO $qt$new_table$qt"; + +} + 1; =pod diff --git a/lib/SQL/Translator/Schema/Table.pm b/lib/SQL/Translator/Schema/Table.pm index e1bac58..3aa2c4c 100644 --- a/lib/SQL/Translator/Schema/Table.pm +++ b/lib/SQL/Translator/Schema/Table.pm @@ -51,9 +51,6 @@ use base 'SQL::Translator::Schema::Object'; use vars qw( $VERSION $FIELD_ORDER ); -$VERSION = sprintf "%d.%02d", q$Revision: 1.37 $ =~ /(\d+)\.(\d+)/; - - # Stringify to our name, being careful not to pass any args through so we don't # accidentally set it to undef. We also have to tweak bool so the object is # still true when it doesn't have a name (which shouldn't happen!). diff --git a/t/30sqlt-new-diff-mysql.t b/t/30sqlt-new-diff-mysql.t index 0927d7d..a4cca87 100644 --- a/t/30sqlt-new-diff-mysql.t +++ b/t/30sqlt-new-diff-mysql.t @@ -49,10 +49,12 @@ CREATE TABLE added ( SET foreign_key_checks=1; +ALTER TABLE old_name RENAME TO new_name; ALTER TABLE employee DROP FOREIGN KEY FK5302D47D93FE702E; ALTER TABLE person DROP UNIQUE UC_age_name; ALTER TABLE person DROP INDEX u_name; ALTER TABLE employee DROP COLUMN job_title; +ALTER TABLE new_name ADD COLUMN new_field integer; ALTER TABLE person ADD COLUMN is_rock_star tinyint(4) DEFAULT '1'; ALTER TABLE person CHANGE COLUMN person_id person_id integer(11) NOT NULL auto_increment; ALTER TABLE person CHANGE COLUMN name name varchar(20) NOT NULL; @@ -92,6 +94,8 @@ SET foreign_key_checks=1; ALTER TABLE employee DROP COLUMN job_title; +ALTER TABLE old_name RENAME TO new_name, + ADD COLUMN new_field integer; ALTER TABLE person DROP UNIQUE UC_age_name, ADD COLUMN is_rock_star tinyint(4) DEFAULT '1', CHANGE COLUMN person_id person_id integer(11) NOT NULL auto_increment, @@ -125,6 +129,9 @@ eq_or_diff($out, <<'## END OF DIFF', "No differences found"); or die $tr->error; my $out = $t->translate( catfile($Bin, qw/data mysql create.sql/ ) ) or die $tr->error; + + # Lets remove the renamed table so we dont have to change the SQL or other tests + $target_schema->drop_table('new_name'); my $schema = $t->schema; unless ( $schema->name ) { diff --git a/t/30sqlt-new-diff-sqlite.t b/t/30sqlt-new-diff-sqlite.t index 7488c1d..04679a4 100644 --- a/t/30sqlt-new-diff-sqlite.t +++ b/t/30sqlt-new-diff-sqlite.t @@ -48,10 +48,12 @@ CREATE TABLE added ( ); +ALTER TABLE old_name RENAME TO new_name; DROP INDEX FK5302D47D93FE702E ON employee; DROP INDEX UC_age_name ON person; DROP INDEX u_name ON person; -- SQL::Translator::Producer::SQLite cant drop_field +ALTER TABLE new_name ADD COLUMN new_field int; ALTER TABLE person ADD COLUMN is_rock_star tinyint(4) DEFAULT '1'; -- SQL::Translator::Producer::SQLite cant alter_field -- SQL::Translator::Producer::SQLite cant rename_field @@ -96,6 +98,8 @@ CREATE TABLE employee ( INSERT INTO employee SELECT position, employee_id FROM employee_temp_alter; DROP TABLE employee_temp_alter; +ALTER TABLE old_name RENAME TO new_name; +ALTER TABLE new_name ADD COLUMN new_field int; CREATE TEMPORARY TABLE person_temp_alter ( person_id INTEGER PRIMARY KEY NOT NULL, name varchar(20) NOT NULL, diff --git a/t/data/diff/create1.yml b/t/data/diff/create1.yml index 8e08999..0319b00 100644 --- a/t/data/diff/create1.yml +++ b/t/data/diff/create1.yml @@ -97,6 +97,20 @@ schema: options: - ENGINE: InnoDB order: 2 + old_name: + name: old_name + fields: + pk: + data_type: int + default_value: ~ + extra: {} + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 1 + name: pk + order: 1 + order: 4 person: constraints: - deferrable: 1 diff --git a/t/data/diff/create2.yml b/t/data/diff/create2.yml index 444a447..897dc62 100644 --- a/t/data/diff/create2.yml +++ b/t/data/diff/create2.yml @@ -76,6 +76,26 @@ schema: options: - ENGINE: InnoDB order: 2 + new_name: + name: new_name + extra: + renamed_from: old_name + fields: + pk: + data_type: int + default_value: ~ + extra: {} + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 1 + name: pk + order: 1 + other: + data_type: int + name: new_field + order: 2 + order: 4 person: constraints: - deferrable: 1