Add transforms for column renames etc
[dbsrgits/DBIx-Class-DeploymentHandler.git] / lib / DBIx / Class / DeploymentHandler / DeployMethod / SQL / Translator.pm
index dfc0b5e..3dc9d9b 100644 (file)
@@ -25,6 +25,12 @@ use File::Spec::Functions;
 
 with 'DBIx::Class::DeploymentHandler::HandlesDeploy';
 
+has ignore_ddl => (
+  isa      => 'Bool',
+  is       => 'ro',
+  default  => undef,
+);
+
 has schema => (
   isa      => 'DBIx::Class::Schema',
   is       => 'ro',
@@ -96,10 +102,17 @@ method __ddl_consume_with_prefix($type, $versions, $prefix) {
     croak "neither $main or $generic exist; please write/generate some SQL";
   }
 
-  opendir my($dh), $dir;
-  my %files = map { $_ => "$dir/$_" } grep { /\.(?:sql|pl|sql-\w+)$/ && -f "$dir/$_" } readdir $dh;
-  closedir $dh;
-
+  my %files;
+  try {
+     opendir my($dh), $dir;
+     %files =
+       map { $_ => "$dir/$_" }
+       grep { /\.(?:sql|pl|sql-\w+)$/ && -f "$dir/$_" }
+       readdir $dh;
+     closedir $dh;
+  } catch {
+    die $_ unless $self->ignore_ddl;
+  };
   if (-d $common) {
     opendir my($dh), $common;
     for my $filename (grep { /\.(?:sql|pl)$/ && -f catfile($common,$_) } readdir $dh) {
@@ -121,8 +134,36 @@ method _ddl_schema_consume_filenames($type, $version) {
   $self->__ddl_consume_with_prefix($type, [ $version ], 'schema')
 }
 
+method _ddl_protoschema_up_consume_filenames($versions) {
+  my $base_dir = $self->script_directory;
+
+  my $dir = catfile( $base_dir, '_protoschema', 'up', join q(-), @{$versions});
+
+  return [] unless -d $dir;
+
+  opendir my($dh), $dir;
+  my %files = map { $_ => "$dir/$_" } grep { /\.pl$/ && -f "$dir/$_" } readdir $dh;
+  closedir $dh;
+
+  return [@files{sort keys %files}]
+}
+
+method _ddl_protoschema_down_consume_filenames($versions) {
+  my $base_dir = $self->script_directory;
+
+  my $dir = catfile( $base_dir, '_protoschema', 'down', join q(-), @{$versions});
+
+  return [] unless -d $dir;
+
+  opendir my($dh), $dir;
+  my %files = map { $_ => "$dir/$_" } grep { /\.pl$/ && -f "$dir/$_" } readdir $dh;
+  closedir $dh;
+
+  return [@files{sort keys %files}]
+}
+
 method _ddl_protoschema_produce_filename($version) {
-  my $dirname = catfile( $self->script_directory, '_protoschema', $version );
+  my $dirname = catfile( $self->script_directory, '_protoschema', 'schema',  $version );
   mkpath($dirname) unless -d $dirname;
 
   return catfile( $dirname, '001-auto.yml' );
@@ -208,13 +249,18 @@ method _run_perl($filename) {
   }
 }
 
-method _run_sql_and_perl($filenames) {
+method _run_sql_and_perl($filenames, $sql_to_run) {
   my @files   = @{$filenames};
   my $guard   = $self->schema->txn_scope_guard if $self->txn_wrap;
 
-  my $sql = '';
+  $self->_run_sql_array($sql_to_run) if $self->ignore_ddl;
+
+  my $sql = ($sql_to_run)?join ";\n", @$sql_to_run:'';
+  FILENAME:
   for my $filename (@files) {
-    if ($filename =~ /\.sql$/) {
+    if ($self->ignore_ddl && $filename =~ /^[^_]*-auto.*\.sql$/) {
+      next FILENAME
+    } elsif ($filename =~ /\.sql$/) {
        $sql .= $self->_run_sql($filename)
     } elsif ( $filename =~ /\.pl$/ ) {
        $self->_run_perl($filename)
@@ -232,11 +278,17 @@ sub deploy {
   my $self = shift;
   my $version = (shift @_ || {})->{version} || $self->schema_version;
   log_info { "deploying version $version" };
-
+  my $sqlt_type = $self->storage->sqlt_type;
+  my $sql;
+  if ($self->ignore_ddl) {
+     $sql = $self->_sql_from_yaml({},
+       '_ddl_protoschema_produce_filename', $sqlt_type
+     );
+  }
   return $self->_run_sql_and_perl($self->_ddl_schema_consume_filenames(
-    $self->storage->sqlt_type,
+    $sqlt_type,
     $version,
-  ));
+  ), $sql);
 }
 
 sub preinstall {
@@ -273,41 +325,116 @@ sub preinstall {
   }
 }
 
-sub _prepare_install {
-  my $self      = shift;
-  my $sqltargs  = { %{$self->sql_translator_args}, %{shift @_} };
-  my $from_file = shift;
-  my $to_file   = shift;
-  my $schema    = $self->schema;
-  my $databases = $self->databases;
+method _sqldiff_from_yaml($from_version, $to_version, $db, $direction) {
   my $dir       = $self->script_directory;
+  my $sqltargs = {
+    add_drop_table => 1,
+    ignore_constraint_names => 1,
+    ignore_index_names => 1,
+    %{$self->sql_translator_args}
+  };
+
+  my $source_schema;
+  {
+    my $prefilename = $self->_ddl_protoschema_produce_filename($from_version, $dir);
+
+    # should probably be a croak
+    carp("No previous schema file found ($prefilename)")
+       unless -e $prefilename;
+
+    my $t = SQL::Translator->new({
+       %{$sqltargs},
+       debug => 0,
+       trace => 0,
+       parser => 'SQL::Translator::Parser::YAML',
+    });
+
+    my $out = $t->translate( $prefilename )
+      or croak($t->error);
+
+    $source_schema = $t->schema;
+
+    $source_schema->name( $prefilename )
+      unless  $source_schema->name;
+  }
+
+  my $dest_schema;
+  {
+    my $filename = $self->_ddl_protoschema_produce_filename($to_version, $dir);
+
+    # should probably be a croak
+    carp("No next schema file found ($filename)")
+       unless -e $filename;
+
+    my $t = SQL::Translator->new({
+       %{$sqltargs},
+       debug => 0,
+       trace => 0,
+       parser => 'SQL::Translator::Parser::YAML',
+    });
+
+    my $out = $t->translate( $filename )
+      or croak($t->error);
+
+    $dest_schema = $t->schema;
+
+    $dest_schema->name( $filename )
+      unless $dest_schema->name;
+  }
+
+  my $transform_files_method =  "_ddl_protoschema_${direction}_consume_filenames";
+  my $transforms = $self->_coderefs_per_files(
+    $self->$transform_files_method([$from_version, $to_version])
+  );
+  $_->($source_schema, $dest_schema) for @$transforms;
+
+  return [SQL::Translator::Diff::schema_diff(
+     $source_schema, $db,
+     $dest_schema,   $db,
+     $sqltargs
+  )];
+}
+
+method _sql_from_yaml($sqltargs, $from_file, $db) {
+  my $schema    = $self->schema;
   my $version   = $self->schema_version;
 
   my $sqlt = SQL::Translator->new({
-    add_drop_table          => 1,
+    add_drop_table          => 0,
     parser                  => 'SQL::Translator::Parser::YAML',
-    %{$sqltargs}
+    %{$sqltargs},
+    producer => $db,
   });
 
   my $yaml_filename = $self->$from_file($version);
 
+  my @sql = $sqlt->translate($yaml_filename);
+  if(!@sql) {
+    carp("Failed to translate to $db, skipping. (" . $sqlt->error . ")");
+    return undef;
+  }
+  return \@sql;
+}
+
+sub _prepare_install {
+  my $self      = shift;
+  my $sqltargs  = { %{$self->sql_translator_args}, %{shift @_} };
+  my $from_file = shift;
+  my $to_file   = shift;
+  my $dir       = $self->script_directory;
+  my $databases = $self->databases;
+  my $version   = $self->schema_version;
+
   foreach my $db (@$databases) {
-    $sqlt->reset;
-    $sqlt->producer($db);
+    my $sql = $self->_sql_from_yaml($sqltargs, $from_file, $db ) or next;
 
     my $filename = $self->$to_file($db, $version, $dir);
     if (-e $filename ) {
       carp "Overwriting existing DDL file - $filename";
       unlink $filename;
     }
-
-    my $sql = $sqlt->translate($yaml_filename);
-    if(!$sql) {
-      carp("Failed to translate to $db, skipping. (" . $sqlt->error . ")");
-      next;
-    }
     open my $file, q(>), $filename;
-    print {$file} $sql;
+    print {$file} join ";\n", @$sql;
     close $file;
   }
 }
@@ -391,69 +518,18 @@ sub prepare_downgrade {
   );
 }
 
+method _coderefs_per_files($files) {
+  no warnings 'redefine';
+  [map eval do { local( @ARGV, $/ ) = $_; <> }, @$files]
+}
+
 method _prepare_changegrade($from_version, $to_version, $version_set, $direction) {
   my $schema    = $self->schema;
   my $databases = $self->databases;
   my $dir       = $self->script_directory;
-  my $sqltargs  = $self->sql_translator_args;
 
   my $schema_version = $self->schema_version;
-
-  $sqltargs = {
-    add_drop_table => 1,
-    ignore_constraint_names => 1,
-    ignore_index_names => 1,
-    %{$sqltargs}
-  };
-
   my $diff_file_method = "_ddl_schema_${direction}_produce_filename";
-  my $source_schema;
-  {
-    my $prefilename = $self->_ddl_protoschema_produce_filename($from_version, $dir);
-
-    # should probably be a croak
-    carp("No previous schema file found ($prefilename)")
-       unless -e $prefilename;
-
-    my $t = SQL::Translator->new({
-       %{$sqltargs},
-       debug => 0,
-       trace => 0,
-       parser => 'SQL::Translator::Parser::YAML',
-    });
-
-    my $out = $t->translate( $prefilename )
-      or croak($t->error);
-
-    $source_schema = $t->schema;
-
-    $source_schema->name( $prefilename )
-      unless  $source_schema->name;
-  }
-
-  my $dest_schema;
-  {
-    my $filename = $self->_ddl_protoschema_produce_filename($to_version, $dir);
-
-    # should probably be a croak
-    carp("No next schema file found ($filename)")
-       unless -e $filename;
-
-    my $t = SQL::Translator->new({
-       %{$sqltargs},
-       debug => 0,
-       trace => 0,
-       parser => 'SQL::Translator::Parser::YAML',
-    });
-
-    my $out = $t->translate( $filename )
-      or croak($t->error);
-
-    $dest_schema = $t->schema;
-
-    $dest_schema->name( $filename )
-      unless $dest_schema->name;
-  }
   foreach my $db (@$databases) {
     my $diff_file = $self->$diff_file_method($db, $version_set, $dir );
     if(-e $diff_file) {
@@ -461,13 +537,8 @@ method _prepare_changegrade($from_version, $to_version, $version_set, $direction
       unlink $diff_file;
     }
 
-    my $diff = SQL::Translator::Diff::schema_diff(
-       $source_schema, $db,
-       $dest_schema,   $db,
-       $sqltargs
-    );
     open my $file, q(>), $diff_file;
-    print {$file} $diff;
+    print {$file} join ";\n", @{$self->_sqldiff_from_yaml($from_version, $to_version, $db, $direction)};
     close $file;
   }
 }
@@ -495,10 +566,17 @@ sub downgrade_single_step {
   my $version_set = (shift @_)->{version_set};
   Dlog_info { "downgrade_single_step'ing $_" } $version_set;
 
+  my $sqlt_type = $self->storage->sqlt_type;
+  my $sql_to_run;
+  if ($self->ignore_ddl) {
+     $sql_to_run = $self->_sqldiff_from_yaml(
+       $version_set->[0], $version_set->[1], $sqlt_type, 'down',
+     );
+  }
   my $sql = $self->_run_sql_and_perl($self->_ddl_schema_down_consume_filenames(
-    $self->storage->sqlt_type,
+    $sqlt_type,
     $version_set,
-  ));
+  ), $sql_to_run);
 
   return ['', $sql];
 }
@@ -508,10 +586,17 @@ sub upgrade_single_step {
   my $version_set = (shift @_)->{version_set};
   Dlog_info { "upgrade_single_step'ing $_" } $version_set;
 
+  my $sqlt_type = $self->storage->sqlt_type;
+  my $sql_to_run;
+  if ($self->ignore_ddl) {
+     $sql_to_run = $self->_sqldiff_from_yaml(
+       $version_set->[0], $version_set->[1], $sqlt_type, 'up',
+     );
+  }
   my $sql = $self->_run_sql_and_perl($self->_ddl_schema_up_consume_filenames(
-    $self->storage->sqlt_type,
+    $sqlt_type,
     $version_set,
-  ));
+  ), $sql_to_run);
   return ['', $sql];
 }