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
fields_to_rename
fields_to_drop
table_options
+ table_renamed_from
/;
__PACKAGE__->mk_accessors(@diff_arrays, 'table_diff_hash');
$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
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);
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;
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;
}
} 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};
}
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{$_} }
: die "$producer_class cant $_";
} else { () }
- } qw/alter_drop_constraint
+ } qw/rename_table
+ alter_drop_constraint
alter_drop_index
drop_field
add_field
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} || []} ) {
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;
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);
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 );
=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
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
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;
# 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 {
sub batch_alter_table {
my ($table, $diffs) = @_;
- $DB::single = 1 if $table->name eq 'deleted';
-
# If we have any of the following
#
# rename_field
# 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, "");
}
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
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!).
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;
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,
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 ) {
);
+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
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,
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
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