notes from ribasushi
[dbsrgits/DBIx-Class-DeploymentHandler.git] / lib / DBIx / Class / DeploymentHandler / DeployMethod / SQL / Translator.pm
index d2d08c0..02684fd 100644 (file)
@@ -5,10 +5,20 @@ use Try::Tiny;
 use SQL::Translator;
 require SQL::Translator::Diff;
 require DBIx::Class::Storage;   # loaded for type constraint
+use autodie;
+use File::Path;
 
 with 'DBIx::Class::DeploymentHandler::HandlesDeploy';
+
 use Carp 'carp';
 
+has schema => (
+  isa      => 'DBIx::Class::Schema',
+  is       => 'ro',
+  required => 1,
+  handles => [qw( schema_version )],
+);
+
 has storage => (
   isa        => 'DBIx::Class::Storage',
   is         => 'ro',
@@ -40,27 +50,87 @@ has databases => (
   default => sub { [qw( MySQL SQLite PostgreSQL )] },
 );
 
-has schema => (
-  isa      => 'DBIx::Class::Schema',
-  is       => 'ro',
-  required => 1,
-  handles => [qw( schema_version )],
-);
-
 has _filedata => (
   isa => 'ArrayRef[Str]',
   is  => 'rw',
 );
 
-method _ddl_filename($type, $versions, $dir) {
-  my $filename = ref $self->schema;
-  $filename =~ s/::/-/g;
+method __ddl_in_with_prefix($type, $versions, $prefix) {
+  my $base_dir = $self->upgrade_directory;
+
+  my $main    = File::Spec->catfile( $base_dir, $type                         );
+  my $generic = File::Spec->catfile( $base_dir, '_generic'                    );
+  my $common =  File::Spec->catfile( $base_dir, '_common', $prefix, join q(-), @{$versions} );
+
+  my $dir;
+  if (-d $main) {
+    $dir = File::Spec->catfile($main, $prefix, join q(-), @{$versions})
+  } elsif (-d $generic) {
+    $dir = File::Spec->catfile($main, $prefix, join q(-), @{$versions})
+  } else {
+    die 'PREPARE TO SQL'
+  }
+
+  opendir my($dh), $dir;
+  my %files = map { $_ => "$dir/$_" } grep { /\.sql$/ && -f "$dir/$_" } readdir($dh);
+  closedir $dh;
+
+  if (-d $common) {
+    opendir my($dh), $common;
+    for my $filename (grep { /\.sql$/ && -f "$common/$_" } readdir($dh)) {
+      unless ($files{$filename}) {
+        $files{$filename} = "$common/$_";
+      }
+    }
+    closedir $dh;
+  }
+
+  return [@files{sort keys %files}]
+}
+
+method _ddl_schema_in_filenames($type, $version) {
+  $self->__ddl_in_with_prefix($type, [ $version ], 'schema')
+}
+
+method _ddl_schema_out_filename($type, $version, $dir) {
+  my $dirname = File::Spec->catfile(
+    $dir, $type, 'schema', $version
+  );
+  File::Path::mkpath($dirname) unless -d $dirname;
+
+  return File::Spec->catfile(
+    $dirname, '001-auto.sql'
+  );
+}
+
+method _ddl_schema_up_in_filenames($type, $versions, $dir) {
+  $self->__ddl_in_with_prefix($type, $versions, 'up')
+}
+
+method _ddl_schema_down_in_filenames($type, $versions, $dir) {
+  $self->__ddl_in_with_prefix($type, $versions, 'down')
+}
+
+method _ddl_schema_up_out_filename($type, $versions, $dir) {
+  my $dirname = File::Spec->catfile(
+    $dir, $type, 'up', join( q(-), @{$versions} )
+  );
+  File::Path::mkpath($dirname) unless -d $dirname;
 
-  $filename = File::Spec->catfile(
-    $dir, "$filename-" . join( q(-), @{$versions} ) . "-$type.sql"
+  return File::Spec->catfile(
+    $dirname, '001-auto.sql'
   );
+}
 
-  return $filename;
+method _ddl_schema_down_out_filename($type, $versions, $dir) {
+  my $dirname = File::Spec->catfile(
+    $dir, $type, 'down', join( q(-), @{$versions} )
+  );
+  File::Path::mkpath($dirname) unless -d $dirname;
+
+  return File::Spec->catfile(
+    $dirname, '001-auto.sql'
+  );
 }
 
 method _deployment_statements {
@@ -70,14 +140,17 @@ method _deployment_statements {
   my $sqltargs = $self->sqltargs;
   my $version  = $self->schema_version;
 
-  my $filename = $self->_ddl_filename($type, [ $version ], $dir);
-  if(-f $filename) {
-      my $file;
-      open $file, q(<), $filename
-        or carp "Can't open $filename ($!)";
-      my @rows = <$file>;
-      close $file;
-      return join '', @rows;
+  my @filenames = @{$self->_ddl_schema_in_filenames($type, $version)};
+
+  for my $filename (@filenames) {
+    if(-f $filename) {
+        my $file;
+        open $file, q(<), $filename
+          or carp "Can't open $filename ($!)";
+        my @rows = <$file>;
+        close $file;
+        return join '', @rows;
+    }
   }
 
   # sources needs to be a parser arg, but for simplicty allow at top level
@@ -92,6 +165,19 @@ method _deployment_statements {
     data => $schema,
   );
 
+#< frew> now note that deploy in the same file calls deployment_statements
+#< ribasushi> right
+#< frew> ALWAYS in array context
+#< ribasushi> right, that's the only way
+#< ribasushi> but create_ddl_dir
+#< ribasushi> calls in scalar
+#< ribasushi> because this is how you get stuff writable to a file
+#< ribasushi> in list you get individual statements for dbh->do
+#< frew> right
+#< frew> ok...
+#< frew> so for *me* I need it *always* in scalar
+#< frew> because I *only* use it to generate the file
+#< ribasushi> correct
   my @ret;
   my $wa = wantarray;
   if ($wa) {
@@ -113,6 +199,31 @@ sub _deploy {
 
   my $deploy = sub {
     my $line = shift;
+#< frew> k, also, we filter out comments and transaction stuff and blank lines
+#< frew> is that really necesary?
+#< frew> and what if I want to run my upgrade in a txn?  seems like something you'd
+#        always want to do really
+#< ribasushi> again - some stuff chokes
+#< frew> ok, so I see filtering out -- and \s*
+#< frew> but I think the txn filtering should be optional and default to NOT filter it
+#        out
+#< ribasushi> then you have a problem
+#< frew> tell me
+#< ribasushi> someone runs a deploy in txn_do
+#< ribasushi> the inner begin will blow up
+#< frew> because it's a nested TXN?
+#< ribasushi> (you an't begin twice on most dbs)
+#< ribasushi> right
+#< ribasushi> on sqlite - for sure
+#< frew> so...read the docs and set txn_filter to true?
+#< ribasushi> more like wrap deploy in a txn
+#< frew> I like that better
+#< ribasushi> and make sure the ddl has no literal txns in them
+#< frew> sure
+#< ribasushi> this way you have stuff under control
+#< frew> so we have txn_wrap default to true
+#< frew> and if people wanna do that by hand they can
+
     return if(!$line || $line =~ /^--|^BEGIN TRANSACTION|^COMMIT|^\s+$/);
     $storage->_query_start($line);
     try {
@@ -144,18 +255,19 @@ sub prepare_install {
   my $databases = $self->databases;
   my $dir       = $self->upgrade_directory;
   my $sqltargs  = $self->sqltargs;
+  my $version = $schema->schema_version;
+
   unless( -d $dir ) {
     carp "Upgrade directory $dir does not exist, using ./\n";
-    $dir = "./";
+    $dir = './';
   }
 
-  my $version = $schema->schema_version;
 
   my $sqlt = SQL::Translator->new({
-    add_drop_table => 1,
+    add_drop_table          => 1,
     ignore_constraint_names => 1,
-    ignore_index_names => 1,
-    parser => 'SQL::Translator::Parser::DBIx::Class',
+    ignore_index_names      => 1,
+    parser                  => 'SQL::Translator::Parser::DBIx::Class',
     %{$sqltargs || {}}
   });
 
@@ -167,7 +279,7 @@ sub prepare_install {
     $sqlt->{schema} = $sqlt_schema;
     $sqlt->producer($db);
 
-    my $filename = $self->_ddl_filename($db, [ $version ], $dir);
+    my $filename = $self->_ddl_schema_out_filename($db, $version, $dir);
     if (-e $filename ) {
       carp "Overwriting existing DDL file - $filename";
       unlink $filename;
@@ -188,7 +300,7 @@ sub prepare_install {
   }
 }
 
-sub prepare_update {
+sub prepare_upgrade {
   my ($self, $from_version, $to_version, $version_set) = @_;
 
   $from_version ||= $self->db_version;
@@ -228,15 +340,15 @@ sub prepare_update {
     $sqlt->{schema} = $sqlt_schema;
     $sqlt->producer($db);
 
-    my $prefilename = $self->_ddl_filename($db, [ $from_version ], $dir);
+    my $prefilename = $self->_ddl_schema_out_filename($db, $from_version, $dir);
     unless(-e $prefilename) {
       carp("No previous schema file found ($prefilename)");
       next;
     }
 
-    my $diff_file = $self->_ddl_filename($db, $version_set, $dir );
+    my $diff_file = $self->_ddl_schema_up_out_filename($db, $version_set, $dir );
     if(-e $diff_file) {
-      carp("Overwriting existing diff file - $diff_file");
+      carp("Overwriting existing up-diff file - $diff_file");
       unlink $diff_file;
     }
 
@@ -275,7 +387,7 @@ sub prepare_update {
       $t->parser( $db ) # could this really throw an exception?
         or $self->throw_exception ($t->error);
 
-      my $filename = $self->_ddl_filename($db, [ $to_version ], $dir);
+      my $filename = $self->_ddl_schema_out_filename($db, $to_version, $dir);
       my $out = $t->translate( $filename )
         or $self->throw_exception ($t->error);
 
@@ -320,20 +432,21 @@ method _read_sql_file($file) {
 sub _upgrade_single_step {
   my $self = shift;
   my @version_set = @{ shift @_ };
-  my $upgrade_file = $self->_ddl_filename(
+  my @upgrade_files = @{$self->_ddl_schema_up_in_filenames(
     $self->storage->sqlt_type,
     \@version_set,
-    $self->upgrade_directory,
-  );
+  )};
 
-  unless (-f $upgrade_file) {
-    # croak?
-    carp "Upgrade not possible, no upgrade file found ($upgrade_file), please create one\n";
-    return;
-  }
+  for my $upgrade_file (@upgrade_files) {
+    unless (-f $upgrade_file) {
+      # croak?
+      carp "Upgrade not possible, no upgrade file found ($upgrade_file), please create one\n";
+      return;
+    }
 
-  $self->_filedata($self->_read_sql_file($upgrade_file)); # I don't like this --fREW 2010-02-22
-  $self->schema->txn_do(sub { $self->_do_upgrade });
+    $self->_filedata($self->_read_sql_file($upgrade_file)); # I don't like this --fREW 2010-02-22
+    $self->schema->txn_do(sub { $self->_do_upgrade });
+  }
 }
 
 method _do_upgrade { $self->_run_upgrade(qr/.*?/) }
@@ -354,8 +467,6 @@ method _apply_statement($statement) {
   $self->storage->dbh->do($_) or carp "SQL was: $_"
 }
 
-__PACKAGE__->meta->make_immutable;
-
 1;
 
 __END__