sqlt-diff option to quote names
[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., 59 Temple Place, Suite 330, Boston, MA
19 # 02111-1307  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 SQL::Translator;
112 use SQL::Translator::Diff;
113 use SQL::Translator::Schema::Constants;
114
115 use vars qw( $VERSION );
116 $VERSION = '1.59';
117
118 my ( @input, $list, $help, $debug, $trace, $caseopt, $ignore_index_names,
119     $ignore_constraint_names, $output_db, $mysql_parser_version,
120     $ignore_view_sql, $ignore_proc_sql, $no_batch_alters, $quote
121 );
122 for my $arg ( @ARGV ) {
123     if ( $arg =~ m/^-?-l(ist)?$/ ) {
124         $list = 1;
125     }
126     elsif ( $arg =~ m/^-?-h(elp)?$/ ) {
127         $help = 1;
128     }
129     elsif ( $arg =~ m/^-?-d(ebug)?$/ ) {
130         $debug = 1;
131     }
132     elsif ( $arg =~ m/^-?-t(race)?$/ ) {
133         $trace = 1;
134     }
135     elsif ( $arg =~ m/^-?-c(ase-insensitive)?$/ ) {
136         $caseopt = 1;
137     }
138     elsif ( $arg =~ m/^--ignore-index-names$/ ) {
139         $ignore_index_names = 1;
140     }
141     elsif ( $arg =~ m/^--ignore-constraint-names$/ ) {
142         $ignore_constraint_names = 1;
143     }
144     elsif ( $arg =~ m/^--mysql-parser-version=(.+)$/ ) {
145         $mysql_parser_version = $1;
146     }
147     elsif ( $arg =~ m/^--output-db=(.+)$/ ) {
148         $output_db = $1;
149     }
150     elsif ( $arg =~ m/^--ignore-view-sql$/ ) {
151         $ignore_view_sql = 1;
152     }
153     elsif ( $arg =~ m/^--ignore-proc-sql$/ ) {
154         $ignore_proc_sql = 1;
155     }
156     elsif ( $arg =~ m/^--quote=(.)$/ ) {
157         $quote = $1;
158     }
159     elsif ( $arg =~ m/^([^=]+)=(.+)$/ ) {
160         push @input, { file => $1, parser => $2 };
161     }
162     elsif ( $arg =~ m/^--no-batch-alters$/ ) {
163         $no_batch_alters = 1;
164     }
165     else {
166         pod2usage( msg => "Unknown argument '$arg'" );
167     }
168 }
169
170 print STDERR <<'EOM' unless $ENV{SQLT_NEWDIFF_NOWARN};
171 This code is experimental, currently the new code only supports MySQL or
172 SQLite diffing. To add support for other databases, please patch the relevant
173 SQL::Translator::Producer:: module.  If you need compatibility with the old
174 sqlt-diff, please use sqlt-diff-old, and look into helping us make this one
175 work for you
176 EOM
177
178 pod2usage(1) if $help || !@ARGV;
179 pod2usage('Please specify only two schemas to diff') if scalar @input > 2;
180
181 my $tr            = SQL::Translator->new;
182 my @parsers       = $tr->list_parsers;
183 my %valid_parsers = map { $_, 1 } @parsers;
184
185 if ( $list ) {
186     print "\nParsers:\n", map { "\t$_\n" } sort @parsers;
187     print "\n";
188     exit(0);
189 }
190
191 pod2usage( msg => 'Too many file args' ) if @input > 2;
192
193 my ( $source_schema, $source_db, $target_schema, $target_db ) = map {
194     my $file   = $_->{'file'};
195     my $parser = $_->{'parser'};
196
197     die "Unable to read file '$file'\n" unless -r $file;
198     die "'$parser' is an invalid parser\n" unless $valid_parsers{ $parser };
199
200     my $t = SQL::Translator->new(parser_args => {mysql_parser_version => $mysql_parser_version});
201     $t->debug( $debug );
202     $t->trace( $trace );
203     $t->parser( $parser )            or die $tr->error;
204     my $out = $t->translate( $file ) or die $tr->error;
205     my $schema = $t->schema;
206     unless ( $schema->name ) {
207         $schema->name( $file );
208     }
209
210     ($schema, $parser);
211 } @input;
212
213 my $result = SQL::Translator::Diff::schema_diff($source_schema, $source_db,
214                                                 $target_schema, $target_db,
215                                                 { caseopt                 => $caseopt,
216                                                   ignore_index_names      => $ignore_index_names,
217                                                   ignore_constraint_names => $ignore_constraint_names,
218                                                   ignore_view_sql         => $ignore_view_sql,
219                                                   ignore_proc_sql         => $ignore_proc_sql,
220                                                   output_db               => $output_db,
221                                                   no_batch_alters         => $no_batch_alters,
222                                                   debug                   => $debug,
223                                                   trace                   => $trace,
224                                                   producer_args => {
225                                                       quote_table_names   => $quote,
226                                                       quote_field_names   => $quote,
227                                                   },
228                                                   });
229 if($result)
230 {
231     print $result;
232 }
233 else
234 {
235     print "No differences found.";
236 }
237
238 # -------------------------------------------------------------------
239 # Bring out number weight & measure in a year of dearth.
240 # William Blake
241 # -------------------------------------------------------------------
242
243 =pod
244
245 =head1 AUTHOR
246
247 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
248
249 =head1 SEE ALSO
250
251 SQL::Translator, L<http://sqlfairy.sourceforge.net>.
252
253 =cut