bd267e3591d5f7eb84a3ea64b7253fc8ac9ed677
[dbsrgits/SQL-Translator.git] / script / sqlt-diff
1 #!/usr/bin/env perl
2 # vim: set ft=perl:
3
4 # -------------------------------------------------------------------
5 # Copyright (C) 2002-2009 The SQLFairy Authors
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation; version 2.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MAb
19 # 02110-1301 USA.
20 # -------------------------------------------------------------------
21
22 =head1 NAME
23
24 sqlt-diff - find the differences b/w two schemas
25
26 =head1 SYNOPSIS
27
28 For help:
29
30   sqlt-diff -h|--help
31
32 For a list of all valid parsers:
33
34   sqlt -l|--list
35
36 To diff two schemas:
37
38   sqlt-diff [options] file_name1=parser1 file_name2=parser2
39
40 Options:
41
42   -d|--debug   Show debugging info
43   -t|--trace   Turn on tracing for Parse::RecDescent
44   -c|--case-insensitive   Compare tables/columns case-insensitively
45   --ignore-index-names    Ignore index name differences
46   --ignore-constraint-names   Ignore constraint name differences
47   --mysql_parser_version=<#####> Specify a target MySQL parser version
48                                  for dealing with /*! comments
49   --output-db=<Producer>  This Producer will be used instead of one
50                           corresponding to parser1 to format output
51                           for new tables
52   --ignore-view-sql    Ignore view SQL differences
53   --ignore-proc-sql    Ignore procedure SQL differences
54   --no-batch-alters    Do not clump multile alters to the same table into a
55                        single ALTER TABLE statement where possible.
56   --quote=<character>  Use <character> to quote all table and field
57                        names in statements
58
59 =head1 DESCRIPTION
60
61 sqlt-diff is a utility for creating a file of SQL commands necessary to
62 transform the first schema provided to the second.  While not yet
63 exhaustive in its ability to mutate the entire schema, it will report the
64 following
65
66 =over
67
68 =item * New tables
69
70 Using the Producer class of the target (second) schema, any tables missing
71 in the first schema will be generated in their entirety (fields, constraints,
72 indices).
73
74 =item * Missing/altered fields
75
76 Any fields missing or altered between the two schemas will be reported
77 as:
78
79   ALTER TABLE <table_name>
80     [DROP <field_name>]
81     [CHANGE <field_name> <datatype> (<size>)] ;
82
83 =item * Missing/altered indices
84
85 Any indices missing or of a different type or on different fields will be
86 indicated.  Indices that should be dropped will be reported as such:
87
88   DROP INDEX <index_name> ON <table_name> ;
89
90 An index of a different type or on different fields will be reported as a
91 new index as such:
92
93   CREATE [<index_type>] INDEX [<index_name>] ON <table_name>
94     ( <field_name>[,<field_name>] ) ;
95
96 =back
97
98 ALTER, CREATE, DROP statements are created by
99 SQL::Translator::Producer::*, see there for support/problems.
100
101 Currently (v0.0900), only MySQL is supported by this code.
102
103 =cut
104
105 # -------------------------------------------------------------------
106
107 use strict;
108 use warnings;
109 use Pod::Usage;
110 use Data::Dumper;
111 use Getopt::Long;
112 use SQL::Translator;
113 use SQL::Translator::Diff;
114 use SQL::Translator::Schema::Constants;
115
116 use vars qw( $VERSION );
117 $VERSION = '1.60';
118
119 my ( @input, $list, $help, $debug, $trace, $caseopt, $ignore_index_names,
120     $ignore_constraint_names, $output_db, $mysql_parser_version,
121     $ignore_view_sql, $ignore_proc_sql, $no_batch_alters, $quote
122 );
123
124 GetOptions(
125     'l|list' => \$list,
126     'h|help' => \$help,
127     'd|debug' => \$debug,
128     't|trace' => \$trace,
129     'c|case-insensitive' => \$caseopt,
130     'ignore-index-names' => \$ignore_index_names,
131     'ignore-constraint-names' => \$ignore_constraint_names,
132     'mysql_parser_version:s' => \$mysql_parser_version,
133     'output-db:s' => \$output_db,
134     'ignore-view-sql' => \$ignore_view_sql,
135     'ignore-proc-sql' => \$ignore_proc_sql,
136     'quote:s' => \$quote,
137     'no-batch-alters' => \$no_batch_alters,
138 ) or pod2usage(2);
139
140 for my $arg ( @ARGV ) {
141     if ( $arg =~ m/^([^=]+)=(.+)$/ ) {
142         push @input, { file => $1, parser => $2 };
143     }
144 }
145
146 my $tr            = SQL::Translator->new;
147 my @parsers       = $tr->list_parsers;
148 my %valid_parsers = map { $_, 1 } @parsers;
149
150
151 if ( $list ) {
152     print "\nParsers:\n", map { "\t$_\n" } sort @parsers;
153     print "\n";
154     exit(0);
155 }
156
157 pod2usage(1) if $help || !@input;
158 pod2usage(msg => 'Please specify two schemas to diff') if scalar @input != 2;
159
160 my ( $source_schema, $source_db, $target_schema, $target_db ) = map {
161     my $file   = $_->{'file'};
162     my $parser = $_->{'parser'};
163
164     die "Unable to read file '$file'\n" unless -r $file;
165     die "'$parser' is an invalid parser\n" unless $valid_parsers{ $parser };
166
167     my $t = SQL::Translator->new(parser_args => {
168         mysql_parser_version => $mysql_parser_version
169     });
170     $t->debug( $debug );
171     $t->trace( $trace );
172     $t->parser( $parser )            or die $tr->error;
173     my $out = $t->translate( $file ) or die $tr->error;
174     my $schema = $t->schema;
175     unless ( $schema->name ) {
176         $schema->name( $file );
177     }
178
179     ($schema, $parser);
180 } @input;
181
182 my $result = SQL::Translator::Diff::schema_diff(
183     $source_schema, $source_db,
184     $target_schema, $target_db,
185     {
186         caseopt                 => $caseopt || 0,
187         ignore_index_names      => $ignore_index_names || 0,
188         ignore_constraint_names => $ignore_constraint_names || 0,
189         ignore_view_sql         => $ignore_view_sql || 0,
190         ignore_proc_sql         => $ignore_proc_sql || 0,
191         output_db               => $output_db,
192         no_batch_alters         => $no_batch_alters || 0,
193         debug                   => $debug || 0,
194         trace                   => $trace || 0,
195         producer_args => {
196             quote_table_names   => $quote || '',
197             quote_field_names   => $quote || '',
198         },
199     }
200 );
201
202 if($result)
203 {
204     print $result;
205 }
206 else
207 {
208     print "No differences found.";
209 }
210
211 # -------------------------------------------------------------------
212 # Bring out number weight & measure in a year of dearth.
213 # William Blake
214 # -------------------------------------------------------------------
215
216 =pod
217
218 =head1 AUTHOR
219
220 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
221
222 =head1 SEE ALSO
223
224 SQL::Translator, L<http://sqlfairy.sourceforge.net>.
225
226 =cut