factor out filesystem interactions
[dbsrgits/DBIx-Class-DeploymentHandler.git] / lib / DBIx / Class / DeploymentHandler / DeployMethod / SQL / Translator.pm
CommitLineData
45d0d9d5 1package DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator;
334bced5 2use Moose;
9af9d0b2 3
9a3a62f1 4# ABSTRACT: Manage your SQL and Perl migrations in nicely laid out directories
5
9af9d0b2 6use autodie;
7use Carp qw( carp croak );
8465e767 8use DBIx::Class::DeploymentHandler::Logger;
c4f51462 9use Log::Contextual qw(:log :dlog), -package_logger =>
8465e767 10 DBIx::Class::DeploymentHandler::Logger->new({
11 env_prefix => 'DBICDH'
12 });
9af9d0b2 13
2e68a8e1 14use Method::Signatures::Simple;
7f50d101 15use Try::Tiny;
9af9d0b2 16
d23c7c77 17use SQL::Translator;
18require SQL::Translator::Diff;
9af9d0b2 19
d23c7c77 20require DBIx::Class::Storage; # loaded for type constraint
41863428 21use DBIx::Class::DeploymentHandler::Types;
22
9af9d0b2 23use File::Path 'mkpath';
24use File::Spec::Functions;
2e68a8e1 25
7521a845 26with 'DBIx::Class::DeploymentHandler::HandlesDeploy';
3c1b5ee8 27
93460690 28has ignore_ddl => (
29 isa => 'Bool',
30 is => 'ro',
31 default => undef,
32);
33
92624ee5 34has force_overwrite => (
35 isa => 'Bool',
36 is => 'ro',
37 default => undef,
38);
39
d54b8d69 40has schema => (
41 isa => 'DBIx::Class::Schema',
42 is => 'ro',
43 required => 1,
d54b8d69 44);
45
334bced5 46has storage => (
47 isa => 'DBIx::Class::Storage',
48 is => 'ro',
49 lazy_build => 1,
50);
51
2eaf903b 52method _build_storage {
53 my $s = $self->schema->storage;
54 $s->_determine_driver;
55 $s
56}
57
02a7b8ac 58has sql_translator_args => (
334bced5 59 isa => 'HashRef',
60 is => 'ro',
61 default => sub { {} },
62);
8a3edced 63
91adde75 64has script_directory => (
334bced5 65 isa => 'Str',
66 is => 'ro',
67 required => 1,
68 default => 'sql',
69);
70
8a3edced 71has _filesystem_interface => (
72 is => 'ro',
73 lazy_build => 1,
74 handles => {
75 _ddl_initialize_consume_filenames => '_ddl_initialize_consume_filenames',
76 _ddl_schema_consume_filenames => '_ddl_schema_consume_filenames',
77 _ddl_protoschema_deploy_consume_filenames => '_ddl_protoschema_deploy_consume_filenames',
78 _ddl_protoschema_upgrade_consume_filenames => '_ddl_protoschema_upgrade_consume_filenames',
79 _ddl_protoschema_downgrade_consume_filenames => '_ddl_protoschema_downgrade_consume_filenames',
80 _ddl_protoschema_produce_filename => '_ddl_protoschema_produce_filename',
81 _ddl_schema_produce_filename => '_ddl_schema_produce_filename',
82 _ddl_schema_upgrade_consume_filenames => '_ddl_schema_upgrade_consume_filenames',
83 _ddl_schema_downgrade_consume_filenames => '_ddl_schema_downgrade_consume_filenames',
84 _ddl_schema_upgrade_produce_filename => '_ddl_schema_upgrade_produce_filename',
85 _ddl_schema_downgrade_produce_filename => '_ddl_schema_downgrade_produce_filename',
86
87 _read_sql_file => '_read_sql_file',
88 _coderefs_per_files => '_coderefs_per_files',
89 _write_data_string => '_write_data_string',
90 _write_data_list => '_write_data_list',
91 },
92);
93
94sub _build__filesystem_interface {
95 use DBIx::Class::DeploymentHandler::Filesystem;
96 DBIx::Class::DeploymentHandler::Filesystem->new(
97 script_directory => $_[0]->script_directory,
98 ignore_ddl => $_[0]->ignore_ddl
99 )
100}
101
334bced5 102has databases => (
103 coerce => 1,
104 isa => 'DBIx::Class::DeploymentHandler::Databases',
105 is => 'ro',
106 default => sub { [qw( MySQL SQLite PostgreSQL )] },
107);
108
a7d53deb 109has txn_wrap => (
110 is => 'ro',
111 isa => 'Bool',
112 default => 1,
113);
114
73caa630 115has schema_version => (
116 is => 'ro',
e86c0c07 117 isa => 'Str',
73caa630 118 lazy_build => 1,
119);
120
6df6dcb9 121# this will probably never get called as the DBICDH
122# will be passing down a schema_version normally, which
123# is built the same way, but we leave this in place
73caa630 124method _build_schema_version { $self->schema->schema_version }
125
f36afe83 126method _run_sql_array($sql) {
41219a5d 127 my $storage = $self->storage;
5d7b27cf 128
1f0d0633 129 $sql = [grep {
130 $_ && # remove blank lines
131 !/^(BEGIN|BEGIN TRANSACTION|COMMIT)/ # strip txn's
132 } map {
133 s/^\s+//; s/\s+$//; # trim whitespace
134 join '', grep { !/^--/ } split /\n/ # remove comments
135 } @$sql];
136
f4075791 137 Dlog_trace { "Running SQL $_" } $sql;
f36afe83 138 foreach my $line (@{$sql}) {
5d7b27cf 139 $storage->_query_start($line);
10a62c3d 140 # the whole reason we do this is so that we can see the line that was run
5d7b27cf 141 try {
5d7b27cf 142 $storage->dbh_do (sub { $_[1]->do($line) });
143 }
144 catch {
10a62c3d 145 die "$_ (running line '$line')"
60e09fce 146 };
5d7b27cf 147 $storage->_query_end($line);
148 }
4d09f712 149 return join "\n", @$sql
f36afe83 150}
151
152method _run_sql($filename) {
f4075791 153 log_debug { "Running SQL from $filename" };
f36afe83 154 return $self->_run_sql_array($self->_read_sql_file($filename));
5d7b27cf 155}
2e68a8e1 156
25c3bec3 157method _run_perl($filename, $versions) {
f4075791 158 log_debug { "Running Perl from $filename" };
5d7b27cf 159 my $filedata = do { local( @ARGV, $/ ) = $filename; <> };
c8a2f7bd 160
5d7b27cf 161 no warnings 'redefine';
162 my $fn = eval "$filedata";
163 use warnings;
f4075791 164 Dlog_trace { "Running Perl $_" } $fn;
5d7b27cf 165
166 if ($@) {
167 carp "$filename failed to compile: $@";
168 } elsif (ref $fn eq 'CODE') {
25c3bec3 169 $fn->($self->schema, $versions)
5d7b27cf 170 } else {
171 carp "$filename should define an anonymouse sub that takes a schema but it didn't!";
172 }
173}
5d7b27cf 174
25c3bec3 175method _run_sql_and_perl($filenames, $sql_to_run, $versions) {
5d7b27cf 176 my @files = @{$filenames};
177 my $guard = $self->schema->txn_scope_guard if $self->txn_wrap;
178
ef44838b 179 $self->_run_sql_array($sql_to_run) if $self->ignore_ddl;
180
181 my $sql = ($sql_to_run)?join ";\n", @$sql_to_run:'';
182 FILENAME:
41219a5d 183 for my $filename (@files) {
ef44838b 184 if ($self->ignore_ddl && $filename =~ /^[^_]*-auto.*\.sql$/) {
185 next FILENAME
186 } elsif ($filename =~ /\.sql$/) {
5d7b27cf 187 $sql .= $self->_run_sql($filename)
398b1385 188 } elsif ( $filename =~ /\.pl$/ ) {
25c3bec3 189 $self->_run_perl($filename, $versions)
41219a5d 190 } else {
fc4b7602 191 croak "A file ($filename) got to deploy that wasn't sql or perl!";
2e68a8e1 192 }
2e68a8e1 193 }
a7d53deb 194
195 $guard->commit if $self->txn_wrap;
41219a5d 196
197 return $sql;
198}
199
200sub deploy {
201 my $self = shift;
be140a5f 202 my $version = (shift @_ || {})->{version} || $self->schema_version;
f4075791 203 log_info { "deploying version $version" };
ef44838b 204 my $sqlt_type = $self->storage->sqlt_type;
205 my $sql;
206 if ($self->ignore_ddl) {
207 $sql = $self->_sql_from_yaml({},
4ea5caf5 208 '_ddl_protoschema_deploy_consume_filenames', $sqlt_type
ef44838b 209 );
210 }
211 return $self->_run_sql_and_perl($self->_ddl_schema_consume_filenames(
212 $sqlt_type,
213 $version,
25c3bec3 214 ), $sql, [$version]);
2e68a8e1 215}
216
ff40cb1f 217sub initialize {
9faec51a 218 my $self = shift;
219 my $args = shift;
220 my $version = $args->{version} || $self->schema_version;
ff40cb1f 221 log_info { "initializing version $version" };
9faec51a 222 my $storage_type = $args->{storage_type} || $self->storage->sqlt_type;
fc4b7602 223
ff40cb1f 224 my @files = @{$self->_ddl_initialize_consume_filenames(
9faec51a 225 $storage_type,
fc4b7602 226 $version,
227 )};
228
229 for my $filename (@files) {
230 # We ignore sql for now (till I figure out what to do with it)
231 if ( $filename =~ /^(.+)\.pl$/ ) {
fc4b7602 232 my $filedata = do { local( @ARGV, $/ ) = $filename; <> };
fc4b7602 233
9faec51a 234 no warnings 'redefine';
5b5defbc 235 my $fn = eval "$filedata";
fc4b7602 236 use warnings;
5b5defbc 237
9faec51a 238 if ($@) {
3fa64c79 239 carp "$filename failed to compile: $@";
9faec51a 240 } elsif (ref $fn eq 'CODE') {
fc4b7602 241 $fn->()
242 } else {
5b5defbc 243 carp "$filename should define an anonymous sub but it didn't!";
fc4b7602 244 }
245 } else {
ff40cb1f 246 croak "A file ($filename) got to initialize_scripts that wasn't sql or perl!";
fc4b7602 247 }
248 }
249}
250
f9c6ab50 251method _sqldiff_from_yaml($from_version, $to_version, $db, $direction) {
91adde75 252 my $dir = $self->script_directory;
28563f97 253 my $sqltargs = {
254 add_drop_table => 1,
255 ignore_constraint_names => 1,
256 ignore_index_names => 1,
257 %{$self->sql_translator_args}
258 };
d54b8d69 259
28563f97 260 my $source_schema;
261 {
262 my $prefilename = $self->_ddl_protoschema_produce_filename($from_version, $dir);
263
264 # should probably be a croak
265 carp("No previous schema file found ($prefilename)")
266 unless -e $prefilename;
267
268 my $t = SQL::Translator->new({
269 %{$sqltargs},
270 debug => 0,
271 trace => 0,
272 parser => 'SQL::Translator::Parser::YAML',
273 });
274
275 my $out = $t->translate( $prefilename )
276 or croak($t->error);
277
278 $source_schema = $t->schema;
279
280 $source_schema->name( $prefilename )
281 unless $source_schema->name;
282 }
283
284 my $dest_schema;
285 {
286 my $filename = $self->_ddl_protoschema_produce_filename($to_version, $dir);
287
288 # should probably be a croak
289 carp("No next schema file found ($filename)")
290 unless -e $filename;
291
292 my $t = SQL::Translator->new({
293 %{$sqltargs},
294 debug => 0,
295 trace => 0,
296 parser => 'SQL::Translator::Parser::YAML',
297 });
298
299 my $out = $t->translate( $filename )
300 or croak($t->error);
301
302 $dest_schema = $t->schema;
303
304 $dest_schema->name( $filename )
305 unless $dest_schema->name;
306 }
f9c6ab50 307
308 my $transform_files_method = "_ddl_protoschema_${direction}_consume_filenames";
309 my $transforms = $self->_coderefs_per_files(
310 $self->$transform_files_method([$from_version, $to_version])
311 );
312 $_->($source_schema, $dest_schema) for @$transforms;
313
28563f97 314 return [SQL::Translator::Diff::schema_diff(
315 $source_schema, $db,
316 $dest_schema, $db,
317 $sqltargs
318 )];
319}
320
321method _sql_from_yaml($sqltargs, $from_file, $db) {
322 my $schema = $self->schema;
323 my $version = $self->schema_version;
93460690 324
4ea5caf5 325 my @sql;
2e68a8e1 326
4ea5caf5 327 my $actual_file = $self->$from_file($version);
328 for my $yaml_filename (@{
329 DlogS_trace { "generating SQL from Serialized SQL Files: $_" }
330 (ref $actual_file?$actual_file:[$actual_file])
331 }) {
332 my $sqlt = SQL::Translator->new({
333 add_drop_table => 0,
334 parser => 'SQL::Translator::Parser::YAML',
335 %{$sqltargs},
336 producer => $db,
337 });
338
339 push @sql, $sqlt->translate($yaml_filename);
340 if(!@sql) {
341 carp("Failed to translate to $db, skipping. (" . $sqlt->error . ")");
342 return undef;
343 }
28563f97 344 }
345 return \@sql;
346}
347
348sub _prepare_install {
349 my $self = shift;
350 my $sqltargs = { %{$self->sql_translator_args}, %{shift @_} };
351 my $from_file = shift;
352 my $to_file = shift;
353 my $dir = $self->script_directory;
354 my $databases = $self->databases;
355 my $version = $self->schema_version;
356
2e68a8e1 357 foreach my $db (@$databases) {
28563f97 358 my $sql = $self->_sql_from_yaml($sqltargs, $from_file, $db ) or next;
2e68a8e1 359
c8a2f7bd 360 my $filename = $self->$to_file($db, $version, $dir);
9600776d 361 if (-e $filename ) {
92624ee5 362 if ($self->force_overwrite) {
363 carp "Overwriting existing DDL file - $filename";
364 unlink $filename;
365 } else {
366 die "Cannot overwrite '$filename', either enable force_overwrite or delete it"
367 }
2e68a8e1 368 }
8a3edced 369
370 $self->_write_data_list($filename, $sql);
2e68a8e1 371 }
372}
373
c8a2f7bd 374sub _resultsource_install_filename {
375 my ($self, $source_name) = @_;
376 return sub {
377 my ($self, $type, $version) = @_;
58eb99c3 378 my $dirname = catfile( $self->script_directory, $type, 'deploy', $version );
c8a2f7bd 379 mkpath($dirname) unless -d $dirname;
380
09bc35e3 381 return catfile( $dirname, "001-auto-$source_name.sql" );
c8a2f7bd 382 }
383}
384
e62add58 385sub _resultsource_protoschema_filename {
386 my ($self, $source_name) = @_;
387 return sub {
388 my ($self, $version) = @_;
d3d6512c 389 my $dirname = catfile( $self->script_directory, '_source', 'deploy', $version );
e62add58 390 mkpath($dirname) unless -d $dirname;
391
392 return catfile( $dirname, "001-auto-$source_name.yml" );
393 }
394}
395
c8a2f7bd 396sub install_resultsource {
be140a5f 397 my ($self, $args) = @_;
ba99ba44 398 my $source = $args->{result_source}
399 or die 'result_source must be passed to install_resultsource';
400 my $version = $args->{version}
401 or die 'version must be passed to install_resultsource';
f4075791 402 log_info { 'installing_resultsource ' . $source->source_name . ", version $version" };
c8a2f7bd 403 my $rs_install_file =
404 $self->_resultsource_install_filename($source->source_name);
405
406 my $files = [
407 $self->$rs_install_file(
408 $self->storage->sqlt_type,
409 $version,
410 )
411 ];
25c3bec3 412 $self->_run_sql_and_perl($files, '', [$version]);
c8a2f7bd 413}
414
415sub prepare_resultsource_install {
416 my $self = shift;
be140a5f 417 my $source = (shift @_)->{result_source};
f4075791 418 log_info { 'preparing install for resultsource ' . $source->source_name };
c8a2f7bd 419
e62add58 420 my $install_filename = $self->_resultsource_install_filename($source->source_name);
421 my $proto_filename = $self->_resultsource_protoschema_filename($source->source_name);
6cae2f56 422 $self->prepare_protoschema({
c8a2f7bd 423 parser_args => { sources => [$source->source_name], }
e62add58 424 }, $proto_filename);
425 $self->_prepare_install({}, $proto_filename, $install_filename);
c8a2f7bd 426}
427
91557c90 428sub prepare_deploy {
f4075791 429 log_info { 'preparing deploy' };
c8a2f7bd 430 my $self = shift;
6776a6d4 431 $self->prepare_protoschema({
432 # Exclude __VERSION so that it gets installed separately
433 parser_args => { sources => [grep { $_ ne '__VERSION' } $self->schema->sources], }
434 }, '_ddl_protoschema_produce_filename');
e62add58 435 $self->_prepare_install({}, '_ddl_protoschema_produce_filename', '_ddl_schema_produce_filename');
c8a2f7bd 436}
437
a41a04e5 438sub prepare_upgrade {
be140a5f 439 my ($self, $args) = @_;
0df68524 440 log_info {
f4075791 441 "preparing upgrade from $args->{from_version} to $args->{to_version}"
0df68524 442 };
be140a5f 443 $self->_prepare_changegrade(
58eb99c3 444 $args->{from_version}, $args->{to_version}, $args->{version_set}, 'upgrade'
be140a5f 445 );
76d311e7 446}
447
448sub prepare_downgrade {
be140a5f 449 my ($self, $args) = @_;
0df68524 450 log_info {
f4075791 451 "preparing downgrade from $args->{from_version} to $args->{to_version}"
0df68524 452 };
be140a5f 453 $self->_prepare_changegrade(
58eb99c3 454 $args->{from_version}, $args->{to_version}, $args->{version_set}, 'downgrade'
be140a5f 455 );
76d311e7 456}
457
458method _prepare_changegrade($from_version, $to_version, $version_set, $direction) {
2e68a8e1 459 my $schema = $self->schema;
460 my $databases = $self->databases;
91adde75 461 my $dir = $self->script_directory;
2e68a8e1 462
73caa630 463 my $schema_version = $self->schema_version;
e62add58 464 my $diff_file_method = "_ddl_schema_${direction}_produce_filename";
e62add58 465 foreach my $db (@$databases) {
466 my $diff_file = $self->$diff_file_method($db, $version_set, $dir );
467 if(-e $diff_file) {
92624ee5 468 if ($self->force_overwrite) {
469 carp("Overwriting existing $direction-diff file - $diff_file");
470 unlink $diff_file;
471 } else {
472 die "Cannot overwrite '$diff_file', either enable force_overwrite or delete it"
473 }
2e68a8e1 474 }
475
8a3edced 476 $self->_write_data_list($diff_file,
477 $self->_sqldiff_from_yaml($from_version, $to_version, $db, $direction)
478 );
2e68a8e1 479 }
480}
481
7d2a6974 482sub downgrade_single_step {
76d311e7 483 my $self = shift;
be140a5f 484 my $version_set = (shift @_)->{version_set};
f4075791 485 Dlog_info { "downgrade_single_step'ing $_" } $version_set;
41219a5d 486
ef44838b 487 my $sqlt_type = $self->storage->sqlt_type;
488 my $sql_to_run;
489 if ($self->ignore_ddl) {
490 $sql_to_run = $self->_sqldiff_from_yaml(
58eb99c3 491 $version_set->[0], $version_set->[1], $sqlt_type, 'downgrade',
ef44838b 492 );
493 }
58eb99c3 494 my $sql = $self->_run_sql_and_perl($self->_ddl_schema_downgrade_consume_filenames(
ef44838b 495 $sqlt_type,
627581cd 496 $version_set,
25c3bec3 497 ), $sql_to_run, $version_set);
3249629f 498
41219a5d 499 return ['', $sql];
76d311e7 500}
501
7d2a6974 502sub upgrade_single_step {
7521a845 503 my $self = shift;
be140a5f 504 my $version_set = (shift @_)->{version_set};
f4075791 505 Dlog_info { "upgrade_single_step'ing $_" } $version_set;
41219a5d 506
ef44838b 507 my $sqlt_type = $self->storage->sqlt_type;
508 my $sql_to_run;
509 if ($self->ignore_ddl) {
510 $sql_to_run = $self->_sqldiff_from_yaml(
58eb99c3 511 $version_set->[0], $version_set->[1], $sqlt_type, 'upgrade',
ef44838b 512 );
513 }
58eb99c3 514 my $sql = $self->_run_sql_and_perl($self->_ddl_schema_upgrade_consume_filenames(
ef44838b 515 $sqlt_type,
627581cd 516 $version_set,
25c3bec3 517 ), $sql_to_run, $version_set);
41219a5d 518 return ['', $sql];
334bced5 519}
520
6cae2f56 521sub prepare_protoschema {
7e08eddd 522 my $self = shift;
e62add58 523 my $sqltargs = { %{$self->sql_translator_args}, %{shift @_} };
524 my $to_file = shift;
7e08eddd 525 my $filename
e62add58 526 = $self->$to_file($self->schema_version);
7e08eddd 527
e62add58 528 # we do this because the code that uses this sets parser args,
529 # so we just need to merge in the package
530 $sqltargs->{parser_args}{package} = $self->schema;
7e08eddd 531 my $sqlt = SQL::Translator->new({
532 parser => 'SQL::Translator::Parser::DBIx::Class',
533 producer => 'SQL::Translator::Producer::YAML',
e62add58 534 %{ $sqltargs },
7e08eddd 535 });
536
537 my $yml = $sqlt->translate;
538
539 croak("Failed to translate to YAML: " . $sqlt->error)
540 unless $yml;
541
542 if (-e $filename ) {
92624ee5 543 if ($self->force_overwrite) {
544 carp "Overwriting existing DDL-YML file - $filename";
545 unlink $filename;
546 } else {
547 die "Cannot overwrite '$filename', either enable force_overwrite or delete it"
548 }
7e08eddd 549 }
550
8a3edced 551 $self->_write_data_string($filename, $yml);
7e08eddd 552}
553
aabd4237 554__PACKAGE__->meta->make_immutable;
555
2e68a8e1 5561;
e051bb00 557
e52174e3 558# vim: ts=2 sw=2 expandtab
559
e051bb00 560__END__
561
bcc72297 562=head1 DESCRIPTION
563
e62add58 564This class is the meat of L<DBIx::Class::DeploymentHandler>. It takes care
565of generating serialized schemata as well as sql files to move from one
566version of a schema to the rest. One of the hallmark features of this class
567is that it allows for multiple sql files for deploy and upgrade, allowing
568developers to fine tune deployment. In addition it also allows for perl
569files to be run at any stage of the process.
bcc72297 570
571For basic usage see L<DBIx::Class::DeploymentHandler::HandlesDeploy>. What's
572documented here is extra fun stuff or private methods.
573
574=head1 DIRECTORY LAYOUT
575
39c88a9a 576Arguably this is the best feature of L<DBIx::Class::DeploymentHandler>.
577It's spiritually based upon L<DBIx::Migration::Directories>, but has a
578lot of extensions and modifications, so even if you are familiar with it,
579please read this. I feel like the best way to describe the layout is with
580the following example:
92c34cab 581
582 $sql_migration_dir
58eb99c3 583 |- _source
584 | |- deploy
03882cab 585 | |- 1
586 | | `- 001-auto.yml
587 | |- 2
588 | | `- 001-auto.yml
589 | `- 3
590 | `- 001-auto.yml
92c34cab 591 |- SQLite
58eb99c3 592 | |- downgrade
4f85efc6 593 | | `- 2-1
e62add58 594 | | `- 001-auto.sql
58eb99c3 595 | |- deploy
92c34cab 596 | | `- 1
e62add58 597 | | `- 001-auto.sql
58eb99c3 598 | `- upgrade
92c34cab 599 | |- 1-2
e62add58 600 | | `- 001-auto.sql
92c34cab 601 | `- 2-3
e62add58 602 | `- 001-auto.sql
92c34cab 603 |- _common
58eb99c3 604 | |- downgrade
4f85efc6 605 | | `- 2-1
92c34cab 606 | | `- 002-remove-customers.pl
58eb99c3 607 | `- upgrade
92c34cab 608 | `- 1-2
25c3bec3 609 | | `- 002-generate-customers.pl
610 | `- _any
611 | `- 999-bump-action.pl
92c34cab 612 `- MySQL
58eb99c3 613 |- downgrade
4f85efc6 614 | `- 2-1
e62add58 615 | `- 001-auto.sql
ff40cb1f 616 |- initialize
80ff6f6d 617 | `- 1
618 | |- 001-create_database.pl
619 | `- 002-create_users_and_permissions.pl
58eb99c3 620 |- deploy
92c34cab 621 | `- 1
e62add58 622 | `- 001-auto.sql
58eb99c3 623 `- upgrade
92c34cab 624 `- 1-2
e62add58 625 `- 001-auto.sql
92c34cab 626
627So basically, the code
628
629 $dm->deploy(1)
630
631on an C<SQLite> database that would simply run
58eb99c3 632C<$sql_migration_dir/SQLite/deploy/1/001-auto.sql>. Next,
92c34cab 633
634 $dm->upgrade_single_step([1,2])
635
58eb99c3 636would run C<$sql_migration_dir/SQLite/upgrade/1-2/001-auto.sql> followed by
25c3bec3 637C<$sql_migration_dir/_common/upgrade/1-2/002-generate-customers.pl>, and
638finally punctuated by
639C<$sql_migration_dir/_common/upgrade/_any/999-bump-action.pl>.
92c34cab 640
0824f31f 641C<.pl> files don't have to be in the C<_common> directory, but most of the time
39c88a9a 642they should be, because perl scripts are generally database independent.
92c34cab 643
ff40cb1f 644Note that unlike most steps in the process, C<initialize> will not run SQL, as
645there may not even be an database at initialize time. It will run perl scripts
80ff6f6d 646just like the other steps in the process, but nothing is passed to them.
647Until people have used this more it will remain freeform, but a recommended use
ff40cb1f 648of initialize is to have it prompt for username and password, and then call the
80ff6f6d 649appropriate C<< CREATE DATABASE >> commands etc.
650
03882cab 651=head2 Directory Specification
652
653The following subdirectories are recognized by this DeployMethod:
654
655=over 2
656
58eb99c3 657=item C<_source> This directory can contain the following directories:
03882cab 658
659=over 2
660
39c88a9a 661=item C<deploy> This directory merely contains directories named after schema
662versions, which in turn contain C<yaml> files that are serialized versions
663of the schema at that version. These files are not for editing by hand.
664
665=back
666
667=item C<_preprocess_schema> This directory can contain the following
668directories:
669
670=over 2
671
58eb99c3 672=item C<downgrade> This directory merely contains directories named after
03882cab 673migrations, which are of the form C<$from_version-$to_version>. Inside of
674these directories you may put Perl scripts which are to return a subref
675that takes the arguments C<< $from_schema, $to_schema >>, which are
676L<SQL::Translator::Schema> objects.
677
58eb99c3 678=item C<upgrade> This directory merely contains directories named after
03882cab 679migrations, which are of the form C<$from_version-$to_version>. Inside of
680these directories you may put Perl scripts which are to return a subref
681that takes the arguments C<< $from_schema, $to_schema >>, which are
682L<SQL::Translator::Schema> objects.
683
03882cab 684=back
685
5b766a24 686=item C<$storage_type> This is a set of scripts that gets run depending on what
687your storage type is. If you are not sure what your storage type is, take a
688look at the producers listed for L<SQL::Translator>. Also note, C<_common>
689is a special case. C<_common> will get merged into whatever other files you
25c3bec3 690already have. This directory can contain the following directories itself:
71d00500 691
692=over 2
693
ff40cb1f 694=item C<initialize> Gets run before the C<deploy> is C<deploy>ed. Has the
58eb99c3 695same structure as the C<deploy> subdirectory as well; that is, it has a
696directory for each schema version. Unlike C<deploy>, C<upgrade>, and C<downgrade>
71d00500 697though, it can only run C<.pl> files, and the coderef in the perl files get
698no arguments passed to them.
699
58eb99c3 700=item C<deploy> Gets run when the schema is C<deploy>ed. Structure is a
71d00500 701directory per schema version, and then files are merged with C<_common> and run
702in filename order. C<.sql> files are merely run, as expected. C<.pl> files are
703run according to L</PERL SCRIPTS>.
704
58eb99c3 705=item C<upgrade> Gets run when the schema is C<upgrade>d. Structure is a directory
71d00500 706per upgrade step, (for example, C<1-2> for upgrading from version 1 to version
7072,) and then files are merged with C<_common> and run in filename order.
708C<.sql> files are merely run, as expected. C<.pl> files are run according
709to L</PERL SCRIPTS>.
710
58eb99c3 711=item C<downgrade> Gets run when the schema is C<downgrade>d. Structure is a directory
71d00500 712per downgrade step, (for example, C<2-1> for downgrading from version 2 to version
7131,) and then files are merged with C<_common> and run in filename order.
714C<.sql> files are merely run, as expected. C<.pl> files are run according
715to L</PERL SCRIPTS>.
716
717
718=back
719
03882cab 720=back
721
25c3bec3 722Note that there can be an C<_any> in the place of any of the versions (like
723C<1-2> or C<1>), which means those scripts will be run B<every> time. So if
724you have an C<_any> in C<_common/upgrade>, that script will get run for every
725upgrade.
726
92c34cab 727=head1 PERL SCRIPTS
728
7d0b0f2b 729A perl script for this tool is very simple. It merely needs to contain an
25c3bec3 730anonymous sub that takes a L<DBIx::Class::Schema> and the version set as it's
731arguments.
732
92c34cab 733A very basic perl script might look like:
734
735 #!perl
736
737 use strict;
738 use warnings;
739
7d0b0f2b 740 sub {
92c34cab 741 my $schema = shift;
742
25c3bec3 743 # [1] for deploy, [1,2] for upgrade or downgrade, probably used with _any
744 my $versions = shift;
745
92c34cab 746 $schema->resultset('Users')->create({
747 name => 'root',
748 password => 'root',
749 })
750 }
bcc72297 751
39c88a9a 752=attr ignore_ddl
753
754This attribute will, when set to true (default is false), cause the DM to use
755L<SQL::Translator> to use the C<_source>'s serialized SQL::Translator::Schema
756instead of any pregenerated SQL. If you have a development server this is
757probably the best plan of action as you will not be putting as many generated
758files in your version control. Goes well with with C<databases> of C<[]>.
759
92624ee5 760=attr force_overwrite
761
762When this attribute is true generated files will be overwritten when the
763methods which create such files are run again. The default is false, in which
764case the program will die with a message saying which file needs to be deleted.
765
eb28403b 766=attr schema
a65184c8 767
bcc72297 768The L<DBIx::Class::Schema> (B<required>) that is used to talk to the database
769and generate the DDL.
770
eb28403b 771=attr storage
a65184c8 772
bcc72297 773The L<DBIx::Class::Storage> that is I<actually> used to talk to the database
774and generate the DDL. This is automatically created with L</_build_storage>.
775
02a7b8ac 776=attr sql_translator_args
cfc9edf9 777
02a7b8ac 778The arguments that get passed to L<SQL::Translator> when it's used.
a65184c8 779
91adde75 780=attr script_directory
cfc9edf9 781
91adde75 782The directory (default C<'sql'>) that scripts are stored in
cfc9edf9 783
eb28403b 784=attr databases
cfc9edf9 785
786The types of databases (default C<< [qw( MySQL SQLite PostgreSQL )] >>) to
787generate files for
788
eb28403b 789=attr txn_wrap
790
bcc72297 791Set to true (which is the default) to wrap all upgrades and deploys in a single
792transaction.
793
73caa630 794=attr schema_version
795
796The version the schema on your harddrive is at. Defaults to
797C<< $self->schema->schema_version >>.