Add renamed_from to tables.
Ash Berlin [Tue, 15 Jan 2008 23:34:54 +0000 (23:34 +0000)]
META.yml
lib/SQL/Translator/Diff.pm
lib/SQL/Translator/Producer/MySQL.pm
lib/SQL/Translator/Producer/SQLite.pm
lib/SQL/Translator/Schema/Table.pm
t/30sqlt-new-diff-mysql.t
t/30sqlt-new-diff-sqlite.t
t/data/diff/create1.yml
t/data/diff/create2.yml

index 32fa024..48dcb35 100644 (file)
--- 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
index 9692e1c..7ee9fc1 100644 (file)
@@ -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<drop_table($table)>
 
+=item * C<rename_table($old_table, $new_table)> (optional)
+
 =item * C<batch_alter_table($table, $hash)> (optional)
 
+
 =back
 
 If the producer supports C<batch_alter_table>, it will be called with the 
index 1252f89..a1f0c37 100644 (file)
@@ -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 {
index dca2aea..149ba7b 100644 (file)
@@ -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
index e1bac58..3aa2c4c 100644 (file)
@@ -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!).
index 0927d7d..a4cca87 100644 (file)
@@ -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 ) {
index 7488c1d..04679a4 100644 (file)
@@ -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,
index 8e08999..0319b00 100644 (file)
@@ -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
index 444a447..897dc62 100644 (file)
@@ -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