Applied patch submitted by Nathan Gray
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Diff.pm
index 9b60803..91b45e1 100644 (file)
@@ -2,389 +2,237 @@ package SQL::Translator::Diff;
 ## SQLT schema diffing code
 use strict;
 use warnings;
+use Data::Dumper;
 use SQL::Translator::Schema::Constants;
 
 sub schema_diff
-{
-#  use Data::Dumper;
+  {
+    #  use Data::Dumper;
+    ## we are getting instructions on how to turn the source into the target
+    ## source == original, target == new (hmm, if I need to comment this, should I rename the vars again ??)
+    ## _schema isa SQL::Translator::Schema
+    ## _db is the name of the producer/db it came out of/into
+    ## results are formatted to the source preferences
+
     my ($source_schema, $source_db, $target_schema, $target_db, $options) = @_;
-#  print Data::Dumper::Dumper($target_schema);
-    my $caseopt = $options->{caseopt} || 0;
+    #     print Data::Dumper::Dumper($target_schema);
+
+    my $producer_class = "SQL::Translator::Producer::$source_db";
+    eval "require $producer_class";
+
+    my $case_insensitive = $options->{caseopt} || 0;
     my $debug = $options->{debug} || 0;
     my $trace = $options->{trace} || 0;
-
-    my $case_insensitive = $source_db =~ /SQLServer/ || $caseopt;
+    my $ignore_index_names = $options->{ignore_index_names} || 0;
+    my $ignore_constraint_names = $options->{ignore_constraint_names} || 0;
+    my $ignore_view_sql = $options->{ignore_view_sql} || 0;
+    my $ignore_proc_sql = $options->{ignore_proc_sql} || 0;
+    my $output_db = $options->{output_db} || $source_db;
 
     my $tar_name  = $target_schema->name;
     my $src_name  = $source_schema->name;
-    my ( @new_tables, @diffs , @diffs_at_end);
+
+    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 );
+    ## do original/source tables exist in target?
     for my $tar_table ( $target_schema->get_tables ) {
-        my $tar_table_name = $tar_table->name;
-        my $src_table      = $source_schema->get_table( $tar_table_name, $case_insensitive );
+      my $tar_table_name = $tar_table->name;
+      my $src_table      = $source_schema->get_table( $tar_table_name, $case_insensitive );
+
+      warn "TABLE '$tar_name.$tar_table_name'\n" if $debug;
+      unless ( $src_table ) {
+        warn "Couldn't find table '$tar_name.$tar_table_name' in '$src_name'\n"
+          if $debug;
+        ## table is new
+        ## add table(s) later. 
+          my $cr_table = $producer_class->can('create_table') || die "$producer_class does not support create_table";
+        my $new_table_sql = $cr_table->($tar_table,  { leave_name => 1 });
+        push (@diffs_new_tables, $new_table_sql);
+        push (@new_tables, $tar_table);
+        next;
+      }
 
-        warn "TABLE '$tar_name.$tar_table_name'\n" if $debug;
-        unless ( $src_table ) {
-            warn "Couldn't find table '$tar_name.$tar_table_name' in '$src_name'\n"
-                if $debug;
-            if ( $source_db =~ /(SQLServer|Oracle)/ ) {
-                for my $constraint ( $tar_table->get_constraints ) {
-                    next if $constraint->type ne FOREIGN_KEY;
-                    push @diffs_at_end, "ALTER TABLE $tar_table_name ADD ".
-                        constraint_to_string($constraint, $source_db, $target_schema).";";
-                    $tar_table->drop_constraint($constraint);
-                }
+      # Go through our options
+      my $options_different = 0;
+      my %checkedOptions;
+    OPTION:
+      for my $tar_table_option_ref ( $tar_table->options ) {
+        my($key_tar, $value_tar) = %{$tar_table_option_ref};
+        for my $src_table_option_ref ( $src_table->options ) {
+          my($key_src, $value_src) = %{$src_table_option_ref};
+          if ( $key_tar eq $key_src ) {
+            if ( defined $value_tar != defined $value_src ) {
+              $options_different = 1;
+              last OPTION;
             }
-            push @new_tables, $tar_table;
-            next;
-        }
-
-        # Go through our options
-        my $options_different = 0;
-        my %checkedOptions;
-      OPTION:
-        for my $tar_table_option_ref ( $tar_table->options ) {
-            my($key_tar, $value_tar) = %{$tar_table_option_ref};
-            for my $src_table_option_ref ( $src_table->options ) {
-                my($key_src, $value_src) = %{$src_table_option_ref};
-                if ( $key_tar eq $key_src ) {
-                    if ( defined $value_tar != defined $value_src ) {
-                        $options_different = 1;
-                        last OPTION;
-                    }
-                    if ( defined $value_tar && $value_tar ne $value_src ) {
-                        $options_different = 1;
-                        last OPTION;
-                    }
-                    $checkedOptions{$key_tar} = 1;
-                    next OPTION;
-                }
-            }
-            $options_different = 1;
-            last OPTION;
-        }
-        # Go through the other table's options
-        unless ( $options_different ) {
-            for my $src_table_option_ref ( $src_table->options ) {
-                my($key, $value) = %{$src_table_option_ref};
-                next if $checkedOptions{$key};
-                $options_different = 1;
-                last;
+            if ( defined $value_tar && $value_tar ne $value_src ) {
+              $options_different = 1;
+              last OPTION;
             }
+            $checkedOptions{$key_tar} = 1;
+            next OPTION;
+          }
         }
-        # If there's a difference, just re-set all the options
-        my @diffs_table_options;
-        if ( $options_different ) {
-            my @options = ();
-            foreach my $option_ref ( $tar_table->options ) {
-                my($key, $value) = %{$option_ref};
-                push(@options, defined $value ? "$key=$value" : $key);
-            }
-            my $options = join(' ', @options);
-            @diffs_table_options = ("ALTER TABLE $tar_table_name $options;");
+        $options_different = 1;
+        last OPTION;
+      }
+      # Go through the other table's options
+      unless ( $options_different ) {
+        for my $src_table_option_ref ( $src_table->options ) {
+          my($key, $value) = %{$src_table_option_ref};
+          next if $checkedOptions{$key};
+          $options_different = 1;
+          last;
         }
+      }
+      # If there's a difference, just re-set all the options
+      if ( $options_different ) {
+        my $al_table = $producer_class->can('alter_table') || die "$producer_class does not support alter_table";
+        my $alter_sql = $al_table->( $tar_table ) . ';';
+        @diffs_table_options = ("$alter_sql");
+      }
 
-        my $src_table_name = $src_table->name;
-        my(@diffs_table_adds, @diffs_table_changes);
-        for my $tar_table_field ( $tar_table->get_fields ) {
-            my $f_tar_type      = $tar_table_field->data_type;
-            my $f_tar_size      = $tar_table_field->size;
-            my $f_tar_name      = $tar_table_field->name;
-            my $f_tar_nullable  = $tar_table_field->is_nullable;
-            my $f_tar_default   = $tar_table_field->default_value;
-            my $f_tar_auto_inc  = $tar_table_field->is_auto_increment;
-            my $src_table_field     = $src_table->get_field( $f_tar_name, $case_insensitive );
-            my $f_tar_full_name = "$tar_name.$tar_table_name.$tar_table_name";
-            warn "FIELD '$f_tar_full_name'\n" if $debug;
+      my $src_table_name = $src_table->name;
+      ## Compare fields, their types, defaults, sizes etc etc
+      for my $tar_table_field ( $tar_table->get_fields ) {
+        my $f_tar_type      = $tar_table_field->data_type;
+        my $f_tar_size      = $tar_table_field->size;
+        my $f_tar_name      = $tar_table_field->name;
+        my $f_tar_nullable  = $tar_table_field->is_nullable;
+        my $f_tar_default   = $tar_table_field->default_value;
+        my $f_tar_auto_inc  = $tar_table_field->is_auto_increment;
+        my $src_table_field     = $src_table->get_field( $f_tar_name, $case_insensitive );
+        my $f_tar_full_name = "$tar_name.$tar_table_name.$f_tar_name";
+        warn "FIELD '$f_tar_full_name'\n" if $debug;
+
+        my $f_src_full_name = "$src_name.$src_table_name.$f_tar_name";
+
+        unless ( $src_table_field ) {
+          warn "Couldn't find field '$f_src_full_name' in '$src_table_name'\n" 
+            if $debug;
+
+          my $add_field = $producer_class->can('add_field') || die "$producer_class does not support add_field";
+          my $alter_add_sql = $add_field->( $tar_table_field ) . ';';
+          push (@diffs_table_adds, $alter_add_sql);
+          next;
+        }
 
-            my $f_src_full_name = "$src_name.$src_table_name.$f_tar_name";
+        ## field exists, so what changed? 
+        ## (do we care? just call equals to see IF)
+        if ( !$tar_table_field->equals($src_table_field, $case_insensitive) ) {
+          ## throw all this junk away and call producer->alter_field
+          ## check output same, etc etc
 
-            unless ( $src_table_field ) {
-                warn "Couldn't find field '$f_src_full_name' in '$src_table_name'\n" 
-                    if $debug;
-                my $temp_default_value = 0;
-                if ( $source_db =~ /SQLServer/ && 
-                     !$f_tar_nullable             && 
-                     !defined $f_tar_default ) {
-                    # SQL Server doesn't allow adding non-nullable, non-default columns
-                    # so we add it with a default value, then remove the default value
-                    $temp_default_value = 1;
-                    my(@numeric_types) = qw(decimal numeric float real int bigint smallint tinyint);
-                    $f_tar_default = grep($_ eq $f_tar_type, @numeric_types) ? 0 : '';
-                }
-                push @diffs_table_adds, sprintf
-                    ( "ALTER TABLE %s ADD %s%s %s%s%s%s%s%s;",
-                      $tar_table_name, $source_db =~ /Oracle/ ? '(' : '',
-                      $f_tar_name, $f_tar_type,
-                      ($f_tar_size && $f_tar_type !~ /(blob|text)$/) ? "($f_tar_size)" : '',
-                      !defined $f_tar_default ? ''
-                      : uc $f_tar_default eq 'NULL' ? ' DEFAULT NULL'
-                      : uc $f_tar_default eq 'CURRENT_TIMESTAMP' ? ' DEFAULT CURRENT_TIMESTAMP'
-                      : " DEFAULT '$f_tar_default'",
-                      $f_tar_nullable ? '' : ' NOT NULL',
-                      $f_tar_auto_inc ? ' AUTO_INCREMENT' : '',
-                      $source_db =~ /Oracle/ ? ')' : '',
-                      );
-                if ( $temp_default_value ) {
-                    undef $f_tar_default;
-                    push @diffs_table_adds, sprintf
-                        ( <<END
-DECLARE \@defname VARCHAR(100), \@cmd VARCHAR(1000)
-SET \@defname = 
-(SELECT name 
- FROM sysobjects so JOIN sysconstraints sc
-  ON so.id = sc.constid 
- WHERE object_name(so.parent_obj) = '%s' 
-   AND so.xtype = 'D'
-   AND sc.colid = 
-    (SELECT colid FROM syscolumns 
-     WHERE id = object_id('%s') AND 
-         name = '%s'))
-SET \@cmd = 'ALTER TABLE %s DROP CONSTRAINT '
-+ \@defname
-EXEC(\@cmd)
-END
-                         , $tar_table_name, $tar_table_name, $f_tar_name, $tar_table_name,
-                        );
-                  }
-                next;
-              }
+          my $al_field = $producer_class->can('alter_field') || die "$producer_class does not support alter_field";
+          my $alter_field_sql = $al_field->( $src_table_field, $tar_table_field ) . ';';
+          push (@diffs_table_adds, $alter_field_sql);
+          next;
+        }
+      }
 
-            my $f_src_type = $src_table_field->data_type;
-            my $f_src_size = $src_table_field->size || '';
-            my $f_src_nullable  = $src_table_field->is_nullable;
-            my $f_src_default   = $src_table_field->default_value;
-            my $f_src_auto_inc  = $src_table_field->is_auto_increment;
-            if ( !$tar_table_field->equals($src_table_field, $case_insensitive) ) {
-              # SQLServer timestamp fields can't be altered, so we drop and add instead
-              if ( $source_db =~ /SQLServer/ && $f_src_type eq "timestamp" ) {
-                       push @diffs_table_changes, "ALTER TABLE $tar_table_name DROP COLUMN $f_tar_name;";
-                   push @diffs_table_changes, sprintf
-                  ( "ALTER TABLE %s ADD %s%s %s%s%s%s%s%s;",
-                    $tar_table_name, $source_db =~ /Oracle/ ? '(' : '',
-                    $f_tar_name, $f_tar_type,
-                    ($f_tar_size && $f_tar_type !~ /(blob|text)$/) ? "($f_tar_size)" : '',
-                    !defined $f_tar_default ? ''
-                    : uc $f_tar_default eq 'NULL' ? ' DEFAULT NULL'
-                    : uc $f_tar_default eq 'CURRENT_TIMESTAMP' ? ' DEFAULT CURRENT_TIMESTAMP'
-                    : " DEFAULT '$f_tar_default'",
-                       $f_tar_nullable ? '' : ' NOT NULL',
-                       $f_tar_auto_inc ? ' AUTO_INCREMENT' : '',
-                       $source_db =~ /Oracle/ ? ')' : '',
-                  );
-                   next;
-              }
+      for my $src_table_field ( $src_table->get_fields ) {
+        my $f_src_name      = $src_table_field->name;
+        my $tar_table_field     = $tar_table->get_field( $f_src_name, $case_insensitive );
+        my $f_src_full_name = "$tar_name.$tar_table_name.$f_src_name";
 
-              my $changeText = $source_db =~ /SQLServer/ ? 'ALTER COLUMN' :
-                               $source_db =~ /Oracle/ ? 'MODIFY (' : 'CHANGE';
-              my $nullText = $f_tar_nullable ? '' : ' NOT NULL';
-              $nullText = '' if $source_db =~ /Oracle/ && $f_tar_nullable == $f_src_nullable;
-              push @diffs_table_changes, sprintf
-                ( "ALTER TABLE %s %s %s%s %s%s%s%s%s%s;",
-                  $tar_table_name, $changeText,
-                  $f_tar_name, $source_db =~ /MySQL/ ? " $f_tar_name" : '',
-                  $f_tar_type, ($f_tar_size && $f_tar_type !~ /(blob|text)$/) ? "($f_tar_size)" : '',
-                  $nullText,
-                  !defined $f_tar_default || $source_db =~ /SQLServer/ ? ''
-                  : uc $f_tar_default eq 'NULL' ? ' DEFAULT NULL'
-                  : uc $f_tar_default eq 'CURRENT_TIMESTAMP' ? ' DEFAULT CURRENT_TIMESTAMP'
-                  : " DEFAULT '$f_tar_default'",
-                  $f_tar_auto_inc ? ' AUTO_INCREMENT' : '',
-                  $source_db =~ /Oracle/ ? ')' : '',
-                );
-              if ( defined $f_tar_default && $source_db =~ /SQLServer/ ) {
-               # Adding a column with a default value for SQL Server means adding a 
-               # constraint and setting existing NULLs to the default value
-               push @diffs_table_changes, sprintf
-                  ( "ALTER TABLE %s ADD CONSTRAINT DF_%s_%s %s FOR %s;",
-                    $tar_table_name, $tar_table_name, $f_tar_name, uc $f_tar_default eq 'NULL' ? 'DEFAULT NULL'
-                    : uc $f_tar_default eq 'CURRENT_TIMESTAMP' ? 'DEFAULT CURRENT_TIMESTAMP'
-                       : "DEFAULT '$f_tar_default'", $f_tar_name,
-                  );
-               push @diffs_table_changes, sprintf
-                  ( "UPDATE %s SET %s = %s WHERE %s IS NULL;",
-                    $tar_table_name, $f_tar_name, uc $f_tar_default eq 'NULL' ? 'NULL'
-                       : uc $f_tar_default eq 'CURRENT_TIMESTAMP' ? 'CURRENT_TIMESTAMP'
-                       : "'$f_tar_default'", $f_tar_name,
-                  );
-              }
-            }
-          }
+        unless ( $tar_table_field ) {
+          warn "Couldn't find field '$f_src_full_name' in '$src_table_name'\n" 
+            if $debug;
 
-        my(%checked_indices, @diffs_index_creates, @diffs_index_drops);
-      INDEX:
-        for my $i_tar ( $tar_table->get_indices ) {
-          for my $i_src ( $src_table->get_indices ) {
-                       if ( $i_tar->equals($i_src, $case_insensitive) ) {
-              $checked_indices{$i_src} = 1;
-              next INDEX;
-                       }
-          }
-          push @diffs_index_creates, sprintf
-            ( "CREATE %sINDEX%s ON %s (%s);",
-              $i_tar->type eq NORMAL ? '' : $i_tar->type." ",
-              $i_tar->name ? " ".$i_tar->name : '',
-              $tar_table_name,
-              join(",", $i_tar->fields),
-            );
+          my $dr_field = $producer_class->can('drop_field') || die "$producer_class does not support drop_field";
+          my $alter_drop_sql = $dr_field->( $src_table_field ) . ';';
+          push (@diffs_table_drops, $alter_drop_sql);
+          next;
         }
-      INDEX2:
+      }
+
+      my (%checked_indices);
+    INDEX_CREATE:
+      for my $i_tar ( $tar_table->get_indices ) {
         for my $i_src ( $src_table->get_indices ) {
-          next if $checked_indices{$i_src};
-          for my $i_tar ( $tar_table->get_indices ) {
-                       next INDEX2 if $i_src->equals($i_tar, $case_insensitive);
+          if ( $i_tar->equals($i_src, $case_insensitive, $ignore_index_names) ) {
+            $checked_indices{$i_src} = 1;
+            next INDEX_CREATE;
           }
-          $source_db =~ /SQLServer/
-                       ? push @diffs_index_drops, "DROP INDEX $tar_table_name.".$i_src->name.";"
-              : push @diffs_index_drops, "DROP INDEX ".$i_src->name." on $tar_table_name;";
         }
+        my $al_cr_index = $producer_class->can('alter_create_index') || die "$producer_class does not support alter_create_index";
+        my $create_index_sql = $al_cr_index->( $i_tar ) . ';';
+        push ( @diffs_index_creates, $create_index_sql );
+      }
+    INDEX_DROP:
+      for my $i_src ( $src_table->get_indices ) {
+        next if !$ignore_index_names && $checked_indices{$i_src};
+        for my $i_tar ( $tar_table->get_indices ) {
+          next INDEX_DROP if $i_src->equals($i_tar, $case_insensitive, $ignore_index_names);
+        }
+        my $al_dr_index = $producer_class->can('alter_drop_index') || die "$producer_class does not support alter_drop_index";
+        my $drop_index_sql = $al_dr_index->( $i_src ) . ';';
+        push ( @diffs_index_drops, $drop_index_sql );
+      }
 
-        my(%checked_constraints, @diffs_constraint_drops);
-      CONSTRAINT:
+      my(%checked_constraints);
+      CONSTRAINT_CREATE:
         for my $c_tar ( $tar_table->get_constraints ) {
-          next if $target_db =~ /Oracle/ && 
-            $c_tar->type eq UNIQUE && $c_tar->name =~ /^SYS_/i;
           for my $c_src ( $src_table->get_constraints ) {
-                       if ( $c_tar->equals($c_src, $case_insensitive) ) {
+                        if ( $c_tar->equals($c_src, $case_insensitive, $ignore_constraint_names) ) {
               $checked_constraints{$c_src} = 1;
-              next CONSTRAINT;
-                       }
+              next CONSTRAINT_CREATE;
+                        }
           }
-          push @diffs_at_end, "ALTER TABLE $tar_table_name ADD ".
-                       constraint_to_string($c_tar, $source_db, $target_schema).";";
+          my $al_cr_const = $producer_class->can('alter_create_constraint') || die "$producer_class does not support alter_create_constraint";
+          my $create_constraint_sql = $al_cr_const->( $c_tar, { leave_name => 1 }) . ';';
+          push ( @diffs_constraint_creates, $create_constraint_sql );
         }
-      CONSTRAINT2:
+
+     CONSTRAINT_DROP:
         for my $c_src ( $src_table->get_constraints ) {
-          next if $source_db =~ /Oracle/ && 
-            $c_src->type eq UNIQUE && $c_src->name =~ /^SYS_/i;
-          next if $checked_constraints{$c_src};
+          next if !$ignore_constraint_names && $checked_constraints{$c_src};
           for my $c_tar ( $tar_table->get_constraints ) {
-                       next CONSTRAINT2 if $c_src->equals($c_tar, $case_insensitive);
-          }
-          if ( $c_src->type eq UNIQUE ) {
-                       push @diffs_constraint_drops, "ALTER TABLE $tar_table_name DROP INDEX ".
-              $c_src->name.";";
-          } elsif ( $source_db =~ /SQLServer/ ) {
-                       push @diffs_constraint_drops, "ALTER TABLE $tar_table_name DROP ".$c_src->name.";";
-          } else {
-                       push @diffs_constraint_drops, "ALTER TABLE $tar_table_name DROP ".$c_src->type.
-              ($c_src->type eq FOREIGN_KEY ? " ".$c_src->name : '').";";
+                        next CONSTRAINT_DROP if $c_src->equals($c_tar, $case_insensitive, $ignore_constraint_names);
           }
-        }
 
-        push @diffs, @diffs_index_drops, @diffs_constraint_drops,
-          @diffs_table_options, @diffs_table_adds,
-            @diffs_table_changes, @diffs_index_creates;
-      }
+          my $al_dr_const = $producer_class->can('alter_drop_constraint') || die "$producer_class does not support alter_drop_constraint";
+          my $drop_constraint_sql = $al_dr_const->( $c_src ) . ';';
+          push ( @diffs_constraint_drops, $drop_constraint_sql );
+        }
+    }
 
+    my @diffs_dropped_tables;
     for my $src_table ( $source_schema->get_tables ) {
       my $src_table_name = $src_table->name;
-      my $tar_table      = $target_schema->get_table( $src_table_name, $source_db =~ /SQLServer/ );
+      my $tar_table      = $target_schema->get_table( $src_table_name, $case_insensitive );
 
       unless ( $tar_table ) {
-       if ( $source_db =~ /SQLServer/ ) {
-          for my $constraint ( $src_table->get_constraints ) {
-            next if $constraint->type eq PRIMARY_KEY;
-            push @diffs, "ALTER TABLE $src_table_name DROP ".$constraint->name.";";
-          }
-       }
-        push @diffs_at_end, "DROP TABLE $src_table_name;";
-        next;
-      }
-
-      for my $src_table_field ( $src_table->get_fields ) {
-        my $f_src_name      = $src_table_field->name;
-        my $tar_table_field     = $tar_table->get_field( $f_src_name );
-        unless ( $tar_table_field ) {
-          my $modifier = $source_db =~ /SQLServer/ ? "COLUMN " : '';
-          push @diffs, "ALTER TABLE $src_table_name DROP $modifier$f_src_name;";
+        for my $c_src ( $src_table->get_constraints ) {
+           my $al_dr_const = $producer_class->can('alter_drop_constraint') || die "$producer_class does not support alter_drop_constraint";
+           my $drop_constraint_sql = $al_dr_const->( $c_src ) . ';';
+           push ( @diffs_constraint_drops, $drop_constraint_sql );
         }
-      }
-    }
-
-    if ( @new_tables ) {
-      my $dummytr = SQL::Translator->new;
-      $dummytr->schema->add_table( $_ ) for @new_tables;
-      my $producer = $dummytr->producer( $source_db );
-      unshift @diffs, $producer->( $dummytr );
-    }
-    push(@diffs, @diffs_at_end);
-
-    if ( @diffs ) {
-        return join( "\n", 
-                  "-- Convert schema '$src_name' to '$tar_name':\n", @diffs, "\n"
-                );
-    }
-    return undef;
-}
 
-sub constraint_to_string {
-  my $c = shift;
-  my $source_db = shift;
-  my $schema = shift or die "No schema given";
-  my @fields = $c->field_names or return '';
-
-  if ( $c->type eq PRIMARY_KEY ) {
-    if ( $source_db =~ /Oracle/ ) {
-      return (defined $c->name ? 'CONSTRAINT '.$c->name.' ' : '') .
-        'PRIMARY KEY (' . join(', ', @fields). ')';
-    } else {
-      return 'PRIMARY KEY (' . join(', ', @fields). ')';
-    }
-  }
-  elsif ( $c->type eq UNIQUE ) {
-    if ( $source_db =~ /Oracle/ ) {
-      return (defined $c->name ? 'CONSTRAINT '.$c->name.' ' : '') .
-        'UNIQUE (' . join(', ', @fields). ')';
-    } else {
-      return 'UNIQUE '.
-        (defined $c->name ? $c->name.' ' : '').
-          '(' . join(', ', @fields). ')';
-    }
-  }
-  elsif ( $c->type eq FOREIGN_KEY ) {
-    my $def = join(' ', 
-                   map { $_ || () } 'CONSTRAINT', $c->name, 'FOREIGN KEY' 
-                  );
-
-    $def .= ' (' . join( ', ', @fields ) . ')';
-
-    $def .= ' REFERENCES ' . $c->reference_table;
-
-    my @rfields = map { $_ || () } $c->reference_fields;
-    unless ( @rfields ) {
-      my $rtable_name = $c->reference_table;
-      if ( my $ref_table = $schema->get_table( $rtable_name ) ) {
-        push @rfields, $ref_table->primary_key;
-      }
-      else {
-        warn "Can't find reference table '$rtable_name' " .
-          "in schema\n";
+        push @diffs_dropped_tables, "DROP TABLE $src_table_name;";
+        next;
       }
     }
 
-    if ( @rfields ) {
-      $def .= ' (' . join( ', ', @rfields ) . ')';
-    }
-    else {
-      warn "FK constraint on " . 'some table' . '.' .
-        join('', @fields) . " has no reference fields\n";
-    }
-
-    if ( $c->match_type ) {
-      $def .= ' MATCH ' . 
-        ( $c->match_type =~ /full/i ) ? 'FULL' : 'PARTIAL';
-    }
+    my @diffs;
+    push ( @diffs, @diffs_constraint_drops, @diffs_index_drops, @diffs_table_drops, @diffs_table_adds, @diffs_index_creates, @diffs_constraint_creates, @diffs_table_options );
+    unshift (@diffs, "SET foreign_key_checks=0;\n\n", @diffs_new_tables, "SET foreign_key_checks=1;\n\n" );
+    push (@diffs, @diffs_dropped_tables);
 
-    if ( $c->on_delete ) {
-      $def .= ' ON DELETE '.join( ' ', $c->on_delete );
+    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 )
+    {
+        @diffs = ('No differences found');
     }
 
-    if ( $c->on_update ) {
-      $def .= ' ON UPDATE '.join( ' ', $c->on_update );
+    if ( @diffs ) {
+#      if ( $target_db !~ /^(MySQL|SQLServer|Oracle)$/ ) {
+      if ( $target_db !~ /^(MySQL)$/ ) {
+        unshift(@diffs, "-- Target database $target_db is untested/unsupported!!!");
+      }
+      return join( "\n", 
+                   "-- Convert schema '$src_name' to '$tar_name':\n", @diffs, "\n"
+                 );
     }
-
-    return $def;
+    return undef;
   }
-}
 
 1;