Allows schema-qualified table names.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Diff.pm
CommitLineData
51ffe5ee 1package SQL::Translator::Diff;
2## SQLT schema diffing code
3use strict;
4use warnings;
da5a1bae 5use Data::Dumper;
51ffe5ee 6use SQL::Translator::Schema::Constants;
7
8sub schema_diff
da5a1bae 9 {
10 # use Data::Dumper;
11 ## we are getting instructions on how to turn the source into the target
12 ## source == original, target == new (hmm, if I need to comment this, should I rename the vars again ??)
13 ## _schema isa SQL::Translator::Schema
14 ## _db is the name of the producer/db it came out of/into
15 ## results are formatted to the source preferences
16
51ffe5ee 17 my ($source_schema, $source_db, $target_schema, $target_db, $options) = @_;
da5a1bae 18 # print Data::Dumper::Dumper($target_schema);
19
20 my $producer_class = "SQL::Translator::Producer::$source_db";
21 eval "require $producer_class";
22
8b5b4c99 23 my $case_insensitive = $options->{caseopt} || 0;
51ffe5ee 24 my $debug = $options->{debug} || 0;
25 my $trace = $options->{trace} || 0;
d990d84b 26 my $ignore_index_names = $options->{ignore_index_names} || 0;
27 my $ignore_constraint_names = $options->{ignore_constraint_names} || 0;
d1a895ce 28 my $ignore_view_sql = $options->{ignore_view_sql} || 0;
29 my $ignore_proc_sql = $options->{ignore_proc_sql} || 0;
8b5b4c99 30 my $output_db = $options->{output_db} || $source_db;
51ffe5ee 31
32 my $tar_name = $target_schema->name;
33 my $src_name = $source_schema->name;
51ffe5ee 34
da5a1bae 35 my ( @diffs_new_tables, @diffs_at_end, @new_tables, @diffs_index_drops, @diffs_constraint_drops, @diffs_table_drops, @diffs_table_adds, @diffs_index_creates, @diffs_constraint_creates, @diffs_table_options );
36 ## do original/source tables exist in target?
37 for my $tar_table ( $target_schema->get_tables ) {
38 my $tar_table_name = $tar_table->name;
39 my $src_table = $source_schema->get_table( $tar_table_name, $case_insensitive );
40
41 warn "TABLE '$tar_name.$tar_table_name'\n" if $debug;
42 unless ( $src_table ) {
43 warn "Couldn't find table '$tar_name.$tar_table_name' in '$src_name'\n"
44 if $debug;
45 ## table is new
46 ## add table(s) later.
47 my $cr_table = $producer_class->can('create_table') || die "$producer_class does not support create_table";
48 my $new_table_sql = $cr_table->($tar_table, { leave_name => 1 });
49 push (@diffs_new_tables, $new_table_sql);
50 push (@new_tables, $tar_table);
51 next;
52 }
51ffe5ee 53
da5a1bae 54 # Go through our options
55 my $options_different = 0;
56 my %checkedOptions;
57 OPTION:
58 for my $tar_table_option_ref ( $tar_table->options ) {
59 my($key_tar, $value_tar) = %{$tar_table_option_ref};
60 for my $src_table_option_ref ( $src_table->options ) {
61 my($key_src, $value_src) = %{$src_table_option_ref};
62 if ( $key_tar eq $key_src ) {
63 if ( defined $value_tar != defined $value_src ) {
64 $options_different = 1;
65 last OPTION;
51ffe5ee 66 }
da5a1bae 67 if ( defined $value_tar && $value_tar ne $value_src ) {
68 $options_different = 1;
69 last OPTION;
51ffe5ee 70 }
da5a1bae 71 $checkedOptions{$key_tar} = 1;
72 next OPTION;
73 }
51ffe5ee 74 }
da5a1bae 75 $options_different = 1;
76 last OPTION;
77 }
78 # Go through the other table's options
79 unless ( $options_different ) {
80 for my $src_table_option_ref ( $src_table->options ) {
81 my($key, $value) = %{$src_table_option_ref};
82 next if $checkedOptions{$key};
83 $options_different = 1;
84 last;
51ffe5ee 85 }
da5a1bae 86 }
87 # If there's a difference, just re-set all the options
88 if ( $options_different ) {
89 my $al_table = $producer_class->can('alter_table') || die "$producer_class does not support alter_table";
90 my $alter_sql = $al_table->( $tar_table ) . ';';
91 @diffs_table_options = ("$alter_sql");
92 }
51ffe5ee 93
da5a1bae 94 my $src_table_name = $src_table->name;
95 ## Compare fields, their types, defaults, sizes etc etc
96 for my $tar_table_field ( $tar_table->get_fields ) {
97 my $f_tar_type = $tar_table_field->data_type;
98 my $f_tar_size = $tar_table_field->size;
99 my $f_tar_name = $tar_table_field->name;
100 my $f_tar_nullable = $tar_table_field->is_nullable;
101 my $f_tar_default = $tar_table_field->default_value;
102 my $f_tar_auto_inc = $tar_table_field->is_auto_increment;
103 my $src_table_field = $src_table->get_field( $f_tar_name, $case_insensitive );
104 my $f_tar_full_name = "$tar_name.$tar_table_name.$f_tar_name";
105 warn "FIELD '$f_tar_full_name'\n" if $debug;
106
107 my $f_src_full_name = "$src_name.$src_table_name.$f_tar_name";
108
109 unless ( $src_table_field ) {
110 warn "Couldn't find field '$f_src_full_name' in '$src_table_name'\n"
111 if $debug;
112
113 my $add_field = $producer_class->can('add_field') || die "$producer_class does not support add_field";
114 my $alter_add_sql = $add_field->( $tar_table_field ) . ';';
115 push (@diffs_table_adds, $alter_add_sql);
116 next;
117 }
51ffe5ee 118
da5a1bae 119 ## field exists, so what changed?
120 ## (do we care? just call equals to see IF)
121 if ( !$tar_table_field->equals($src_table_field, $case_insensitive) ) {
122 ## throw all this junk away and call producer->alter_field
123 ## check output same, etc etc
51ffe5ee 124
da5a1bae 125 my $al_field = $producer_class->can('alter_field') || die "$producer_class does not support alter_field";
126 my $alter_field_sql = $al_field->( $src_table_field, $tar_table_field ) . ';';
127 push (@diffs_table_adds, $alter_field_sql);
128 next;
129 }
130 }
51ffe5ee 131
da5a1bae 132 for my $src_table_field ( $src_table->get_fields ) {
133 my $f_src_name = $src_table_field->name;
134 my $tar_table_field = $tar_table->get_field( $f_src_name, $case_insensitive );
135 my $f_src_full_name = "$tar_name.$tar_table_name.$f_src_name";
51ffe5ee 136
da5a1bae 137 unless ( $tar_table_field ) {
138 warn "Couldn't find field '$f_src_full_name' in '$src_table_name'\n"
139 if $debug;
51ffe5ee 140
da5a1bae 141 my $dr_field = $producer_class->can('drop_field') || die "$producer_class does not support drop_field";
142 my $alter_drop_sql = $dr_field->( $src_table_field ) . ';';
143 push (@diffs_table_drops, $alter_drop_sql);
144 next;
51ffe5ee 145 }
da5a1bae 146 }
147
148 my (%checked_indices);
149 INDEX_CREATE:
150 for my $i_tar ( $tar_table->get_indices ) {
51ffe5ee 151 for my $i_src ( $src_table->get_indices ) {
da5a1bae 152 if ( $i_tar->equals($i_src, $case_insensitive, $ignore_index_names) ) {
153 $checked_indices{$i_src} = 1;
154 next INDEX_CREATE;
51ffe5ee 155 }
51ffe5ee 156 }
da5a1bae 157 my $al_cr_index = $producer_class->can('alter_create_index') || die "$producer_class does not support alter_create_index";
158 my $create_index_sql = $al_cr_index->( $i_tar ) . ';';
159 push ( @diffs_index_creates, $create_index_sql );
160 }
161 INDEX_DROP:
162 for my $i_src ( $src_table->get_indices ) {
163 next if !$ignore_index_names && $checked_indices{$i_src};
164 for my $i_tar ( $tar_table->get_indices ) {
165 next INDEX_DROP if $i_src->equals($i_tar, $case_insensitive, $ignore_index_names);
166 }
167 my $al_dr_index = $producer_class->can('alter_drop_index') || die "$producer_class does not support alter_drop_index";
168 my $drop_index_sql = $al_dr_index->( $i_src ) . ';';
169 push ( @diffs_index_drops, $drop_index_sql );
170 }
51ffe5ee 171
da5a1bae 172 my(%checked_constraints);
173 CONSTRAINT_CREATE:
51ffe5ee 174 for my $c_tar ( $tar_table->get_constraints ) {
51ffe5ee 175 for my $c_src ( $src_table->get_constraints ) {
da5a1bae 176 if ( $c_tar->equals($c_src, $case_insensitive, $ignore_constraint_names) ) {
51ffe5ee 177 $checked_constraints{$c_src} = 1;
da5a1bae 178 next CONSTRAINT_CREATE;
179 }
51ffe5ee 180 }
da5a1bae 181 my $al_cr_const = $producer_class->can('alter_create_constraint') || die "$producer_class does not support alter_create_constraint";
182 my $create_constraint_sql = $al_cr_const->( $c_tar, { leave_name => 1 }) . ';';
183 push ( @diffs_constraint_creates, $create_constraint_sql );
51ffe5ee 184 }
da5a1bae 185
186 CONSTRAINT_DROP:
51ffe5ee 187 for my $c_src ( $src_table->get_constraints ) {
03739eb3 188 next if !$ignore_constraint_names && $checked_constraints{$c_src};
51ffe5ee 189 for my $c_tar ( $tar_table->get_constraints ) {
da5a1bae 190 next CONSTRAINT_DROP if $c_src->equals($c_tar, $case_insensitive, $ignore_constraint_names);
51ffe5ee 191 }
51ffe5ee 192
da5a1bae 193 my $al_dr_const = $producer_class->can('alter_drop_constraint') || die "$producer_class does not support alter_drop_constraint";
194 my $drop_constraint_sql = $al_dr_const->( $c_src ) . ';';
195 push ( @diffs_constraint_drops, $drop_constraint_sql );
196 }
197 }
51ffe5ee 198
da5a1bae 199 my @diffs_dropped_tables;
51ffe5ee 200 for my $src_table ( $source_schema->get_tables ) {
201 my $src_table_name = $src_table->name;
8b5b4c99 202 my $tar_table = $target_schema->get_table( $src_table_name, $case_insensitive );
51ffe5ee 203
204 unless ( $tar_table ) {
da5a1bae 205 for my $c_src ( $src_table->get_constraints ) {
206 my $al_dr_const = $producer_class->can('alter_drop_constraint') || die "$producer_class does not support alter_drop_constraint";
207 my $drop_constraint_sql = $al_dr_const->( $c_src ) . ';';
208 push ( @diffs_constraint_drops, $drop_constraint_sql );
51ffe5ee 209 }
51ffe5ee 210
da5a1bae 211 push @diffs_dropped_tables, "DROP TABLE $src_table_name;";
212 next;
7ac784ff 213 }
7ac784ff 214 }
215
da5a1bae 216 my @diffs;
217 push ( @diffs, @diffs_constraint_drops, @diffs_index_drops, @diffs_table_drops, @diffs_table_adds, @diffs_index_creates, @diffs_constraint_creates, @diffs_table_options );
218 unshift (@diffs, "SET foreign_key_checks=0;\n\n", @diffs_new_tables, "SET foreign_key_checks=1;\n\n" );
219 push (@diffs, @diffs_dropped_tables);
7ac784ff 220
da5a1bae 221 if(@diffs_constraint_drops+@diffs_index_drops+@diffs_table_drops+@diffs_table_adds+@diffs_index_creates+@diffs_constraint_creates+@diffs_table_options+@diffs_new_tables+@diffs_dropped_tables == 0 )
222 {
223 @diffs = ('No differences found');
51ffe5ee 224 }
51ffe5ee 225
da5a1bae 226 if ( @diffs ) {
227# if ( $target_db !~ /^(MySQL|SQLServer|Oracle)$/ ) {
228 if ( $target_db !~ /^(MySQL)$/ ) {
229 unshift(@diffs, "-- Target database $target_db is untested/unsupported!!!");
51ffe5ee 230 }
da5a1bae 231 return join( "\n",
232 "-- Convert schema '$src_name' to '$tar_name':\n", @diffs, "\n"
233 );
51ffe5ee 234 }
da5a1bae 235 return undef;
51ffe5ee 236 }
51ffe5ee 237
2381;