with 'DBIx::Class::DeploymentHandler::HandlesDeploy';
+has ignore_ddl => (
+ isa => 'Bool',
+ is => 'ro',
+ default => undef,
+);
+
has schema => (
isa => 'DBIx::Class::Schema',
is => 'ro',
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) {
$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' );
}
}
-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)
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 {
}
}
-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;
}
}
);
}
+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) {
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;
}
}
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];
}
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];
}
like the best way to describe the layout is with the following example:
$sql_migration_dir
+ |- _protoschema
+ | |- schema
+ | |- 1
+ | | `- 001-auto.yml
+ | |- 2
+ | | `- 001-auto.yml
+ | `- 3
+ | `- 001-auto.yml
|- SQLite
| |- down
| | `- 2-1
of preinstall is to have it prompt for username and password, and then call the
appropriate C<< CREATE DATABASE >> commands etc.
+=head2 Directory Specification
+
+The following subdirectories are recognized by this DeployMethod:
+
+=over 2
+
+=item C<_protoschema> This directory can have the following subdirs:
+
+=over 2
+
+=item C<down> This directory merely contains directories named after
+migrations, which are of the form C<$from_version-$to_version>. Inside of
+these directories you may put Perl scripts which are to return a subref
+that takes the arguments C<< $from_schema, $to_schema >>, which are
+L<SQL::Translator::Schema> objects.
+
+=item C<up> This directory merely contains directories named after
+migrations, which are of the form C<$from_version-$to_version>. Inside of
+these directories you may put Perl scripts which are to return a subref
+that takes the arguments C<< $from_schema, $to_schema >>, which are
+L<SQL::Translator::Schema> objects.
+
+=item C<schema> This directory merely contains directories named after schema
+versions, which in turn contain C<yaml> files that are serialized versions
+of the schema at that version. These files are not for editing by hand.
+
+=back
+
+=back
+
=head1 PERL SCRIPTS
A perl script for this tool is very simple. It merely needs to contain an