From: Ken Youens-Clark Date: Fri, 6 Feb 2004 17:38:56 +0000 (+0000) Subject: Changed to print out schema mutation statements (CREATE/ALTER/DROP) instead X-Git-Tag: v0.06~212 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=942485ea80b6c01ad2fa862295ea12e17f597fe8;p=dbsrgits%2FSQL-Translator.git Changed to print out schema mutation statements (CREATE/ALTER/DROP) instead of just English descriptions. Also made it a one-way comparison (how to change schema1 into schema2) rather than two-way comparison (just a description of what's different between the two). --- diff --git a/bin/sqlt-diff b/bin/sqlt-diff index 1c54abb..2d85ad6 100755 --- a/bin/sqlt-diff +++ b/bin/sqlt-diff @@ -2,7 +2,7 @@ # vim: set ft=perl: # ------------------------------------------------------------------- -# $Id: sqlt-diff,v 1.2 2004-01-25 18:16:57 kycl4rk Exp $ +# $Id: sqlt-diff,v 1.3 2004-02-06 17:38:56 kycl4rk Exp $ # ------------------------------------------------------------------- # Copyright (C) 2002 The SQLFairy Authors # @@ -45,8 +45,46 @@ Options: =head1 DESCRIPTION -This script is part of the SQL Fairy project. It will find the -differences between two schemas. +sqlt-diff is a utility for creating a file of SQL commands necessary to +transform the first schema provided to the second. While not yet +exhaustive in its ability to mutate the entire schema, it will report the +following + +=over + +=item * New tables + +Using the Producer class of the target (second) schema, any tables missing +in the first schema will be generated in their entirety (fields, constraints, +indices). + +=item * Missing/altered fields + +Any fields missing or altered between the two schemas will be reported +as: + + ALTER TABLE + [DROP ] + [CHANGE ()] ; + +=item * Missing/altered indices + +Any indices missing or of a different type or on different fields will be +indicated. Indices that should be dropped will be reported as such: + + DROP INDEX ON ; + +An index of a different type or on different fields will be reported as a +new index as such: + + CREATE [] INDEX [] ON + ( [,] ) ; + +=back + +"ALTER/DROP TABLE" and "CREATE INDEX" statements B generated by +the Producer, unfortunately, and may require massaging before being passed to +your target database. =cut @@ -56,9 +94,10 @@ use strict; use Pod::Usage; use Data::Dumper; use SQL::Translator; +use SQL::Translator::Schema::Constants; use vars qw( $VERSION ); -$VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/; +$VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/; my ( @input, $list, $help, $debug ); for my $arg ( @ARGV ) { @@ -80,6 +119,7 @@ for my $arg ( @ARGV ) { } pod2usage(1) if $help; +pod2usage('Please specify only two schemas to diff') if scalar @input > 2; my $tr = SQL::Translator->new; my @parsers = $tr->list_parsers; @@ -93,8 +133,8 @@ if ( $list ) { pod2usage( msg => 'Too many file args' ) if @input > 2; -my ( $schema1, $schema2 ); -my $i = 1; +my ( $source_schema, $source_db, $target_schema, $target_db ); +my $i = 2; for my $in ( @input ) { my $file = $in->{'file'}; my $parser = $in->{'parser'}; @@ -113,70 +153,150 @@ for my $in ( @input ) { } if ( $i == 1 ) { - $schema1 = $schema; + $source_schema = $schema; + $source_db = $parser; } else { - $schema2 = $schema; + $target_schema = $schema; + $target_db = $parser; } - $i++; + $i--; } -#print "Schemas =\n", Dumper( \@schemas ), "\n" if $debug; +my $s1_name = $source_schema->name; +my $s2_name = $target_schema->name; +my ( @new_tables, @diffs ); +for my $t1 ( $source_schema->get_tables ) { + my $t1_name = $t1->name; + my $t2 = $target_schema->get_table( $t1_name ); + + warn "Checking '$s1_name' table '$t1_name'\n" if $debug; + unless ( $t2 ) { + push @new_tables, $t1; + next; + } -my @matrix = ( [ $schema1, $schema2 ], [ $schema2, $schema1 ] ); + my $t2_name = $t2->name; + for my $t1_field ( $t1->get_fields ) { + my $t1_type = $t1_field->data_type; + my $t1_size = $t1_field->size; + my $t1_name = $t1_field->name; + my $t2_field = $t2->get_field( $t1_name ); + my $f1_full_name = "$s1_name.$t1_name.$t1_name"; + warn "Checking '$f1_full_name'\n" if $debug; -my @diffs; -for my $rec ( @matrix ) { - my $s1 = $rec->[0]; - my $s2 = $rec->[1]; - my $s1_name = $s1->name; - my $s2_name = $s2->name; - print "Schema1 = '$s1_name', schema2 = '$s2_name'\n" if $debug; - for my $t1 ( $s1->get_tables ) { - my $t1_name = $t1->name; - my $t2 = $s2->get_table( $t1_name ); + my $f2_full_name = "$s2_name.$t2_name.$t1_name"; - print "Checking '$s1_name' table '$t1_name'\n" if $debug; - unless ( $t2 ) { - push @diffs, "Schema '$s2_name' is missing table '$t1_name'"; + unless ( $t2_field ) { + push @diffs, + "ALTER TABLE $t1_name ADD $t1_name $t1_type($t1_size);"; next; } - my $t2_name = $t2->name; - for my $t1_field ( $t1->get_fields ) { - my $fname = $t1_field->name; - my $t2_field = $t2->get_field( $fname ); - my $f1_full_name = "$s1_name.$t1_name.$fname"; - print "Checking '$f1_full_name'\n" if $debug; - - unless ( $t2_field ) { - push @diffs, - "Table '$s2_name.$t2_name' is missing field '$fname'"; - next; - } + my $t2_type = $t2_field->data_type; + my $t2_size = $t2_field->size; - my $f2_full_name = "$s2_name.$t2_name.$fname"; - my $t1_type = $t1_field->data_type; - my $t1_size = $t1_field->size; - my $t2_type = $t2_field->data_type; - my $t2_size = $t2_field->size; + if ( $t1_type ne $t2_type || + ( defined $t1_size && ( $t1_size ne $t2_size ) ) + ) { + push @diffs, + "ALTER TABLE $t1_name CHANGE $t1_name $t1_type($t1_size);"; + } + } - if ( $t1_type ne $t2_type ) { - push @diffs, "'$f1_full_name' type = '$t1_type' and ". - "'$f2_full_name' type = '$t2_type'"; + my ( %t1_indices, %t2_indices ); + for my $rec ( [ $t1, \%t1_indices ], [ $t2, \%t2_indices ] ) { + my ( $table, $indices ) = @$rec; + for my $index ( $table->get_indices ) { + my $name = $index->name; + my $type = $index->type; + my $fields = join( ',', sort $index->fields ); + + $indices->{'type'}{ $type }{ $fields } = $name; + + if ( $name ) { + $indices->{'name'}{ $name } = { + type => $type, + fields => $fields, + }; } + } + } - if ( defined $t1_size && ( $t1_size ne $t2_size ) ) { - push @diffs, "'$f1_full_name' size = '$t1_size' and ". - "'$f2_full_name' size = '$t2_size'"; + for my $type ( keys %{ $t2_indices{'type'} } ) { + while ( my ($fields, $iname) = each %{$t2_indices{'type'}{ $type } } ) { + if ( $iname ) { + if ( my $i1 = $t1_indices{'name'}{ $iname } ) { + my $i1_type = $i1->{'type'}; + my $i1_fields = $i1->{'fields'}; + if ( $i1_type eq $type && $i1_fields eq $fields ) { + next; + } + } } + elsif ( my $i1 = $t1_indices{'type'}{ $type }{ $fields } ) { + next; + } + + push @diffs, "DROP INDEX $iname on $t1_name;"; } } + + for my $type ( keys %{ $t1_indices{'type'} } ) { + while ( my ($fields, $iname) = each %{$t1_indices{'type'}{ $type } } ) { + if ( $iname ) { + if ( my $i2 = $t2_indices{'name'}{ $iname } ) { + my $i2_type = $i2->{'type'}; + my $i2_fields = $i2->{'fields'}; + if ( $i2_type eq $type && $i2_fields eq $fields ) { + next; + } + } + } + elsif ( my $i2 = $t2_indices{'type'}{ $type }{ $fields } ) { + next; + } + + push @diffs, sprintf( + "CREATE %sINDEX%s ON %s (%s);", + $type eq NORMAL ? '' : "$type ", + $iname ? " $iname" : '', + $t1_name, + $fields, + ); + } + } +} + +for my $t2 ( $target_schema->get_tables ) { + my $t2_name = $t2->name; + my $t1 = $source_schema->get_table( $t2_name ); + + unless ( $t1 ) { + push @diffs, "DROP TABLE $t2_name;"; + next; + } + + for my $t2_field ( $t2->get_fields ) { + my $f2_name = $t2_field->name; + my $t1_field = $t1->get_field( $f2_name ); + unless ( $t1_field ) { + push @diffs, "ALTER TABLE $t2_name DROP $f2_name;"; + } + } +} + +if ( @new_tables ) { + my $dummy_tr = SQL::Translator->new; + $dummy_tr->schema->add_table( $_ ) for @new_tables; + my $producer = $dummy_tr->producer( $target_db ); + unshift @diffs, $producer->( $dummy_tr ); } if ( @diffs ) { - print "Diffs\n-----\n"; - print join( "\n", @diffs, '' ); + print join( "\n", + "-- Convert schema '$s2_name' to '$s1_name':", @diffs, '' + ); } else { print "There were no differences.\n";