Reduce $Id to its normal form
[dbsrgits/SQL-Translator.git] / bin / sqlt-diff
index 4351f1e..61c29a9 100755 (executable)
@@ -2,9 +2,9 @@
 # vim: set ft=perl:
 
 # -------------------------------------------------------------------
-# $Id: sqlt-diff,v 1.1 2003-10-17 16:42:14 kycl4rk Exp $
+# $Id$
 # -------------------------------------------------------------------
-# Copyright (C) 2002 The SQLFairy Authors
+# Copyright (C) 2002-2009 The SQLFairy Authors
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License as
@@ -37,16 +37,68 @@ For a list of all valid parsers:
 
 To diff two schemas:
 
-  sqlt-diff [options] file1=parser file2=parser
+  sqlt-diff [options] file_name1=parser1 file_name2=parser2
 
 Options:
 
   -d|--debug   Show debugging info
+  -t|--trace   Turn on tracing for Parse::RecDescent
+  -c|--case-insensitive   Compare tables/columns case-insensitively
+  --ignore-index-names    Ignore index name differences
+  --ignore-constraint-names   Ignore constraint name differences
+  --mysql_parser_version=<#####> Specify a target MySQL parser version
+                                 for dealing with /*! comments
+  --output-db=<Producer>  This Producer will be used instead of one
+                          corresponding to parser1 to format output
+                          for new tables
+  --ignore-view-sql    Ignore view SQL differences
+  --ignore-proc-sql    Ignore procedure SQL differences
+  --no-batch-alters    Do not clump multile alters to the same table into a
+                       single ALTER TABLE statement where possible.
 
 =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 <table_name> 
+    [DROP <field_name>] 
+    [CHANGE <field_name> <datatype> (<size>)] ;
+
+=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 <index_name> ON <table_name> ;
+
+An index of a different type or on different fields will be reported as a 
+new index as such:
+
+  CREATE [<index_type>] INDEX [<index_name>] ON <table_name> 
+    ( <field_name>[,<field_name>] ) ;
+
+=back
+
+ALTER, CREATE, DROP statements are created by
+SQL::Translator::Producer::*, see there for support/problems.
+
+Currently (v0.0900), only MySQL is supported by this code.
 
 =cut
 
@@ -56,11 +108,15 @@ use strict;
 use Pod::Usage;
 use Data::Dumper;
 use SQL::Translator;
+use SQL::Translator::Diff;
+use SQL::Translator::Schema::Constants;
 
 use vars qw( $VERSION );
-$VERSION = sprintf "%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/;
+$VERSION = '1.99';
 
-my ( @input, $list, $help, $debug );
+my ( @input, $list, $help, $debug, $trace, $caseopt, $ignore_index_names, 
+       $ignore_constraint_names, $output_db, $mysql_parser_version,
+       $ignore_view_sql, $ignore_proc_sql, $no_batch_alters );
 for my $arg ( @ARGV ) {
     if ( $arg =~ m/^-?-l(ist)?$/ ) {
         $list = 1;
@@ -71,15 +127,51 @@ for my $arg ( @ARGV ) {
     elsif ( $arg =~ m/^-?-d(ebug)?$/ ) {
         $debug = 1; 
     }
+    elsif ( $arg =~ m/^-?-t(race)?$/ ) {
+        $trace = 1; 
+    }
+    elsif ( $arg =~ m/^-?-c(ase-insensitive)?$/ ) {
+        $caseopt = 1; 
+    }
+    elsif ( $arg =~ m/^--ignore-index-names$/ ) {
+        $ignore_index_names = 1; 
+    }
+    elsif ( $arg =~ m/^--ignore-constraint-names$/ ) {
+        $ignore_constraint_names = 1; 
+    }
+    elsif ( $arg =~ m/^--mysql-parser-version=(.+)$/ ) {
+        $mysql_parser_version = $1; 
+    }
+    elsif ( $arg =~ m/^--output-db=(.+)$/ ) {
+        $output_db = $1; 
+    }
+    elsif ( $arg =~ m/^--ignore-view-sql$/ ) {
+        $ignore_view_sql = 1; 
+    }
+    elsif ( $arg =~ m/^--ignore-proc-sql$/ ) {
+        $ignore_proc_sql = 1; 
+    }
     elsif ( $arg =~ m/^([^=]+)=(.+)$/ ) {
         push @input, { file => $1, parser => $2 };
     }
+    elsif ( $arg =~ m/^--no-batch-alters$/ ) {
+      $no_batch_alters = 1;
+    }
     else {
         pod2usage( msg => "Unknown argument '$arg'" );
     }
 }
 
-pod2usage(1) if $help;
+print STDERR <<'EOM';
+This code is experimental, currently the new code only supports MySQL or 
+SQLite diffing. To add support for other databases, please patch the relevant
+SQL::Translator::Producer:: module.  If you need compatibility with the old
+sqlt-diff, please use sqlt-diff-old, and look into helping us make this one
+work for you
+EOM
+
+pod2usage(1) if $help || !@ARGV;
+pod2usage('Please specify only two schemas to diff') if scalar @input > 2;
 
 my $tr            = SQL::Translator->new;
 my @parsers       = $tr->list_parsers;
@@ -93,93 +185,44 @@ if ( $list ) {
 
 pod2usage( msg => 'Too many file args' ) if @input > 2;
 
-my ( $schema1, $schema2 );
-my $i = 1;
-for my $in ( @input ) {
-    my $file   = $in->{'file'};
-    my $parser = $in->{'parser'};
+my ( $source_schema, $source_db, $target_schema, $target_db ) = map {
+    my $file   = $_->{'file'};
+    my $parser = $_->{'parser'};
 
     die "Unable to read file '$file'\n" unless -r $file;
     die "'$parser' is an invalid parser\n" unless $valid_parsers{ $parser };
 
-    my $t = SQL::Translator->new;
+    my $t = SQL::Translator->new(parser_args => {mysql_parser_version => $mysql_parser_version});
     $t->debug( $debug );
+    $t->trace( $trace );
     $t->parser( $parser )            or die $tr->error;
-    $t->producer( 'YAML' )           or die $tr->error;
     my $out = $t->translate( $file ) or die $tr->error;
     my $schema = $t->schema;
     unless ( $schema->name ) {
         $schema->name( $file );
     }
 
-    if ( $i == 1 ) {
-        $schema1 = $schema;
-    }
-    else {
-        $schema2 = $schema;
-    }
-    $i++;
-}
-
-#print "Schemas =\n", Dumper( \@schemas ), "\n" if $debug;
-
-my @matrix = ( [ $schema1, $schema2 ], [ $schema2, $schema1 ] );
-
-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 );
-
-        print "Checking '$s1_name' table '$t1_name'\n" if $debug;
-        unless ( $t2 ) {
-            push @diffs, "Schema '$s2_name' is missing table '$t1_name'";
-            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 $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 ) {
-                push @diffs, "'$f1_full_name' type = '$t1_type' and ".
-                    "'$f2_full_name' type = '$t2_type'";
-            }
-
-            if ( defined $t1_size && ( $t1_size ne $t2_size ) ) {
-                push @diffs, "'$f1_full_name' size = '$t1_size' and ".
-                    "'$f2_full_name' size = '$t2_size'";
-            }
-        }
-    }
-}
-
-if ( @diffs ) {
-    print "Diffs\n-----\n";
-    print join( "\n", @diffs, '' );
+    ($schema, $parser);
+} @input;
+
+my $result = SQL::Translator::Diff::schema_diff($source_schema, $source_db, 
+                                                $target_schema, $target_db,
+                                                { caseopt                 => $caseopt,
+                                                  ignore_index_names      => $ignore_index_names,
+                                                  ignore_constraint_names => $ignore_constraint_names,
+                                                  ignore_view_sql         => $ignore_view_sql,
+                                                  ignore_proc_sql         => $ignore_proc_sql,
+                                                  output_db               => $output_db,
+                                                  no_batch_alters         => $no_batch_alters,
+                                                  debug                   => $debug,
+                                                  trace                   => $trace });
+if($result)
+{
+    print $result;
 }
-else {
-    print "There were no differences.\n";
+else
+{
+    print "No differences found.";
 }
 
 # -------------------------------------------------------------------