658b143524ab2a6529536cf0debc019d6ebc162a
[dbsrgits/DBIx-Class-DeploymentHandler.git] / lib / DBIx / Class / DeploymentHandler / DeployMethod / SQL / Translator.pm
1 package DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator;
2 use Moose;
3
4 # ABSTRACT: Manage your SQL and Perl migrations in nicely laid out directories
5
6 use autodie;
7 use Carp qw( carp croak );
8 use DBIx::Class::DeploymentHandler::LogImporter qw(:log :dlog);
9 use Context::Preserve;
10
11 use Try::Tiny;
12
13 use SQL::Translator;
14 require SQL::Translator::Diff;
15
16 require DBIx::Class::Storage;   # loaded for type constraint
17 use DBIx::Class::DeploymentHandler::Types;
18
19 use Path::Class qw(file dir);
20
21 with 'DBIx::Class::DeploymentHandler::HandlesDeploy';
22
23 has ignore_ddl => (
24   isa      => 'Bool',
25   is       => 'ro',
26   default  => undef,
27 );
28
29 has force_overwrite => (
30   isa      => 'Bool',
31   is       => 'ro',
32   default  => undef,
33 );
34
35 has schema => (
36   is       => 'ro',
37   required => 1,
38 );
39
40 has storage => (
41   isa        => 'DBIx::Class::Storage',
42   is         => 'ro',
43   lazy_build => 1,
44 );
45
46 sub _build_storage {
47   my $self = shift;
48   my $s = $self->schema->storage;
49   $s->_determine_driver;
50   $s
51 }
52
53 has sql_translator_args => (
54   isa => 'HashRef',
55   is  => 'ro',
56   default => sub { {} },
57 );
58 has script_directory => (
59   isa      => 'Str',
60   is       => 'ro',
61   required => 1,
62   default  => 'sql',
63 );
64
65 has databases => (
66   coerce  => 1,
67   isa     => 'DBIx::Class::DeploymentHandler::Databases',
68   is      => 'ro',
69   default => sub { [qw( MySQL SQLite PostgreSQL )] },
70 );
71
72 has txn_wrap => (
73   is => 'ro',
74   isa => 'Bool',
75   default => 1,
76 );
77
78 has schema_version => (
79   is => 'ro',
80   isa => 'Str',
81   lazy_build => 1,
82 );
83
84 # this will probably never get called as the DBICDH
85 # will be passing down a schema_version normally, which
86 # is built the same way, but we leave this in place
87 sub _build_schema_version {
88   my $self = shift;
89   $self->schema->schema_version
90 }
91
92 sub __ddl_consume_with_prefix {
93   my ($self, $type, $versions, $prefix) = @_;
94   my $base_dir = $self->script_directory;
95
96   my $main    = dir( $base_dir, $type      );
97   my $common  =
98     dir( $base_dir, '_common', $prefix, join q(-), @{$versions} );
99
100   my $common_any  =
101     dir( $base_dir, '_common', $prefix, '_any' );
102
103   my $dir;
104   if (-d $main) {
105     $dir = dir($main, $prefix, join q(-), @{$versions})
106   } else {
107     if ($self->ignore_ddl) {
108       return []
109     } else {
110       croak "$main does not exist; please write/generate some SQL"
111     }
112   }
113   my $dir_any = dir($main, $prefix, '_any');
114
115   my %files;
116   try {
117      opendir my($dh), $dir;
118      %files =
119        map { $_ => "$dir/$_" }
120        grep { /\.(?:sql|pl|sql-\w+)$/ && -f "$dir/$_" }
121        readdir $dh;
122      closedir $dh;
123   } catch {
124     die $_ unless $self->ignore_ddl;
125   };
126   for my $dirname (grep { -d $_ } $common, $common_any, $dir_any) {
127     opendir my($dh), $dirname;
128     for my $filename (grep { /\.(?:sql|pl)$/ && -f file($dirname,$_) } readdir $dh) {
129       unless ($files{$filename}) {
130         $files{$filename} = file($dirname,$filename);
131       }
132     }
133     closedir $dh;
134   }
135
136   return [@files{sort keys %files}]
137 }
138
139 sub _ddl_initialize_consume_filenames {
140   my ($self, $type, $version) = @_;
141   $self->__ddl_consume_with_prefix($type, [ $version ], 'initialize')
142 }
143
144 sub _ddl_schema_consume_filenames {
145   my ($self, $type, $version) = @_;
146   $self->__ddl_consume_with_prefix($type, [ $version ], 'deploy')
147 }
148
149 sub _ddl_protoschema_deploy_consume_filenames {
150   my ($self, $version) = @_;
151   my $base_dir = $self->script_directory;
152
153   my $dir = dir( $base_dir, '_source', 'deploy', $version);
154   return [] unless -d $dir;
155
156   opendir my($dh), $dir;
157   my %files = map { $_ => "$dir/$_" } grep { /\.yml$/ && -f "$dir/$_" } readdir $dh;
158   closedir $dh;
159
160   return [@files{sort keys %files}]
161 }
162
163 sub _ddl_protoschema_upgrade_consume_filenames {
164   my ($self, $versions) = @_;
165   my $base_dir = $self->script_directory;
166
167   my $dir = dir( $base_dir, '_preprocess_schema', 'upgrade', join q(-), @{$versions});
168
169   return [] unless -d $dir;
170
171   opendir my($dh), $dir;
172   my %files = map { $_ => "$dir/$_" } grep { /\.pl$/ && -f "$dir/$_" } readdir $dh;
173   closedir $dh;
174
175   return [@files{sort keys %files}]
176 }
177
178 sub _ddl_protoschema_downgrade_consume_filenames {
179   my ($self, $versions) = @_;
180   my $base_dir = $self->script_directory;
181
182   my $dir = dir( $base_dir, '_preprocess_schema', 'downgrade', join q(-), @{$versions});
183
184   return [] unless -d $dir;
185
186   opendir my($dh), $dir;
187   my %files = map { $_ => "$dir/$_" } grep { /\.pl$/ && -f "$dir/$_" } readdir $dh;
188   closedir $dh;
189
190   return [@files{sort keys %files}]
191 }
192
193 sub _ddl_protoschema_produce_filename {
194   my ($self, $version) = @_;
195   my $dirname = dir( $self->script_directory, '_source', 'deploy',  $version );
196   $dirname->mkpath unless -d $dirname;
197
198   return "" . file( $dirname, '001-auto.yml' );
199 }
200
201 sub _ddl_schema_produce_filename {
202   my ($self, $type, $version) = @_;
203   my $dirname = dir( $self->script_directory, $type, 'deploy', $version );
204   $dirname->mkpath unless -d $dirname;
205
206   return "" . file( $dirname, '001-auto.sql' );
207 }
208
209 sub _ddl_schema_upgrade_consume_filenames {
210   my ($self, $type, $versions) = @_;
211   $self->__ddl_consume_with_prefix($type, $versions, 'upgrade')
212 }
213
214 sub _ddl_schema_downgrade_consume_filenames {
215   my ($self, $type, $versions) = @_;
216   $self->__ddl_consume_with_prefix($type, $versions, 'downgrade')
217 }
218
219 sub _ddl_schema_upgrade_produce_filename {
220   my ($self, $type, $versions) = @_;
221   my $dir = $self->script_directory;
222
223   my $dirname = dir( $dir, $type, 'upgrade', join q(-), @{$versions});
224   $dirname->mkpath unless -d $dirname;
225
226   return "" . file( $dirname, '001-auto.sql' );
227 }
228
229 sub _ddl_schema_downgrade_produce_filename {
230   my ($self, $type, $versions, $dir) = @_;
231   my $dirname = dir( $dir, $type, 'downgrade', join q(-), @{$versions} );
232   $dirname->mkpath unless -d $dirname;
233
234   return "" . file( $dirname, '001-auto.sql');
235 }
236
237 sub _run_sql_array {
238   my ($self, $sql) = @_;
239   my $storage = $self->storage;
240
241   $sql = [ _split_sql_chunk( @$sql ) ];
242
243   Dlog_trace { "Running SQL $_" } $sql;
244   foreach my $line (@{$sql}) {
245     $storage->_query_start($line);
246     # the whole reason we do this is so that we can see the line that was run
247     try {
248       $storage->dbh_do (sub { $_[1]->do($line) });
249     }
250     catch {
251       die "$_ (running line '$line')"
252     };
253     $storage->_query_end($line);
254   }
255   return join "\n", @$sql
256 }
257
258 # split a chunk o' SQL into statements
259 sub _split_sql_chunk {
260     my @sql = map { split /;\n/, $_ } @_;
261
262     for ( @sql ) {
263         # strip transactions
264         s/^(?:BEGIN|BEGIN TRANSACTION|COMMIT).*//mgi;
265
266         # trim whitespaces
267         s/^\s+//gm;
268         s/\s+$//gm;
269
270         # remove comments
271         s/^--.*//gm;
272
273         # remove blank lines
274         s/^\n//gm;
275
276         # put on single line
277         s/\n/ /g;
278     }
279
280     return grep $_, @sql;
281 }
282
283 sub _run_sql {
284   my ($self, $filename) = @_;
285   log_debug { "Running SQL from $filename" };
286   return $self->_run_sql_array($self->_read_sql_file($filename));
287 }
288
289 sub _load_sandbox {
290   my $_file = shift;
291
292   my $_package = $_file;
293   $_package =~ s/([^A-Za-z0-9_])/sprintf("_%2x", ord($1))/eg;
294
295   my $fn = eval sprintf <<'END_EVAL', $_package;
296 package DBICDH::Sandbox::%s;
297 {
298   our $app;
299   $app ||= require $_file;
300   if ( !$app && ( my $error = $@ || $! )) { die $error; }
301   $app;
302 }
303 END_EVAL
304
305   croak $@ if $@;
306
307   croak "$_file should define an anonymous sub that takes a schema but it didn't!"
308      unless ref $fn && ref $fn eq 'CODE';
309
310   return $fn;
311 }
312
313 sub _run_perl {
314   my ($self, $filename, $versions) = @_;
315   log_debug { "Running Perl from $filename" };
316
317   my $fn = _load_sandbox($filename);
318
319   Dlog_trace { "Running Perl $_" } $fn;
320
321   $fn->($self->schema, $versions)
322 }
323
324 sub txn_do {
325    my ( $self, $code ) = @_;
326    return $code->() unless $self->txn_wrap;
327
328    my $guard = $self->schema->txn_scope_guard;
329
330    return preserve_context { $code->() } after => sub { $guard->commit };
331 }
332
333 sub _run_sql_and_perl {
334   my ($self, $filenames, $sql_to_run, $versions) = @_;
335   my @files   = @{$filenames};
336   $self->txn_do(sub {
337      $self->_run_sql_array($sql_to_run) if $self->ignore_ddl;
338
339      my $sql = ($sql_to_run)?join ";\n", @$sql_to_run:'';
340      FILENAME:
341      for my $filename (map file($_), @files) {
342        if ($self->ignore_ddl && $filename->basename =~ /^[^-]*-auto.*\.sql$/) {
343          next FILENAME
344        } elsif ($filename =~ /\.sql$/) {
345           $sql .= $self->_run_sql($filename)
346        } elsif ( $filename =~ /\.pl$/ ) {
347           $self->_run_perl($filename, $versions)
348        } else {
349          croak "A file ($filename) got to deploy that wasn't sql or perl!";
350        }
351      }
352
353      return $sql;
354   });
355 }
356
357 sub deploy {
358   my $self = shift;
359   my $version = (shift @_ || {})->{version} || $self->schema_version;
360   log_info { "deploying version $version" };
361   my $sqlt_type = $self->storage->sqlt_type;
362   my $sql;
363   my $sqltargs = $self->sql_translator_args;
364   if ($self->ignore_ddl) {
365      $sql = $self->_sql_from_yaml($sqltargs,
366        '_ddl_protoschema_deploy_consume_filenames', $sqlt_type
367      );
368   }
369   return $self->_run_sql_and_perl($self->_ddl_schema_consume_filenames(
370     $sqlt_type,
371     $version,
372   ), $sql, [$version]);
373 }
374
375 sub initialize {
376   my $self         = shift;
377   my $args         = shift;
378   my $version      = $args->{version}      || $self->schema_version;
379   log_info { "initializing version $version" };
380   my $storage_type = $args->{storage_type} || $self->storage->sqlt_type;
381
382   my @files = @{$self->_ddl_initialize_consume_filenames(
383     $storage_type,
384     $version,
385   )};
386
387   for my $filename (@files) {
388     # We ignore sql for now (till I figure out what to do with it)
389     if ( $filename =~ /^(.+)\.pl$/ ) {
390       my $filedata = do { local( @ARGV, $/ ) = $filename; <> };
391
392       no warnings 'redefine';
393       my $fn = eval "$filedata";
394       use warnings;
395
396       if ($@) {
397         croak "$filename failed to compile: $@";
398       } elsif (ref $fn eq 'CODE') {
399         $fn->()
400       } else {
401         croak "$filename should define an anonymous sub but it didn't!";
402       }
403     } else {
404       croak "A file ($filename) got to initialize_scripts that wasn't sql or perl!";
405     }
406   }
407 }
408
409 sub _sqldiff_from_yaml {
410   my ($self, $from_version, $to_version, $db, $direction) = @_;
411   my $dir       = $self->script_directory;
412   my $sqltargs = {
413     add_drop_table => 1,
414     ignore_constraint_names => 1,
415     ignore_index_names => 1,
416     %{$self->sql_translator_args}
417   };
418
419   my $source_schema;
420   {
421     my $prefilename = $self->_ddl_protoschema_produce_filename($from_version, $dir);
422
423     # should probably be a croak
424     carp("No previous schema file found ($prefilename)")
425        unless -e $prefilename;
426
427     my $t = SQL::Translator->new({
428        %{$sqltargs},
429        debug => 0,
430        trace => 0,
431        parser => 'SQL::Translator::Parser::YAML',
432     });
433
434     my $out = $t->translate( $prefilename )
435       or croak($t->error);
436
437     $source_schema = $t->schema;
438
439     $source_schema->name( $prefilename )
440       unless  $source_schema->name;
441   }
442
443   my $dest_schema;
444   {
445     my $filename = $self->_ddl_protoschema_produce_filename($to_version, $dir);
446
447     # should probably be a croak
448     carp("No next schema file found ($filename)")
449        unless -e $filename;
450
451     my $t = SQL::Translator->new({
452        %{$sqltargs},
453        debug => 0,
454        trace => 0,
455        parser => 'SQL::Translator::Parser::YAML',
456     });
457
458     my $out = $t->translate( $filename )
459       or croak($t->error);
460
461     $dest_schema = $t->schema;
462
463     $dest_schema->name( $filename )
464       unless $dest_schema->name;
465   }
466
467   my $transform_files_method =  "_ddl_protoschema_${direction}_consume_filenames";
468   my $transforms = $self->_coderefs_per_files(
469     $self->$transform_files_method([$from_version, $to_version])
470   );
471   $_->($source_schema, $dest_schema) for @$transforms;
472
473   return [SQL::Translator::Diff::schema_diff(
474      $source_schema, $db,
475      $dest_schema,   $db,
476      $sqltargs
477   )];
478 }
479
480 sub _sql_from_yaml {
481   my ($self, $sqltargs, $from_file, $db) = @_;
482   my $schema    = $self->schema;
483   my $version   = $self->schema_version;
484
485   my @sql;
486
487   my $actual_file = $self->$from_file($version);
488   for my $yaml_filename (@{(
489      DlogS_trace { "generating SQL from Serialized SQL Files: $_" }
490         (ref $actual_file?$actual_file:[$actual_file])
491   )}) {
492      my $sqlt = SQL::Translator->new({
493        add_drop_table          => 0,
494        parser                  => 'SQL::Translator::Parser::YAML',
495        %{$sqltargs},
496        producer => $db,
497      });
498
499      push @sql, $sqlt->translate($yaml_filename);
500      if(!@sql) {
501        carp("Failed to translate to $db, skipping. (" . $sqlt->error . ")");
502        return undef;
503      }
504   }
505   return \@sql;
506 }
507
508 sub _prepare_install {
509   my $self      = shift;
510   my $sqltargs  = { %{$self->sql_translator_args}, %{shift @_} };
511   my $from_file = shift;
512   my $to_file   = shift;
513   my $dir       = $self->script_directory;
514   my $databases = $self->databases;
515   my $version   = $self->schema_version;
516
517   foreach my $db (@$databases) {
518     my $sql = $self->_sql_from_yaml($sqltargs, $from_file, $db ) or next;
519
520     my $filename = $self->$to_file($db, $version, $dir);
521     if (-e $filename ) {
522       if ($self->force_overwrite) {
523          carp "Overwriting existing DDL file - $filename";
524          unlink $filename;
525       } else {
526          die "Cannot overwrite '$filename', either enable force_overwrite or delete it"
527       }
528     }
529     open my $file, q(>), $filename;
530     print {$file} join ";\n", @$sql, '';
531     close $file;
532   }
533 }
534
535 sub _resultsource_install_filename {
536   my ($self, $source_name) = @_;
537   return sub {
538     my ($self, $type, $version) = @_;
539     my $dirname = dir( $self->script_directory, $type, 'deploy', $version );
540     $dirname->mkpath unless -d $dirname;
541
542     return "" . file( $dirname, "001-auto-$source_name.sql" );
543   }
544 }
545
546 sub _resultsource_protoschema_filename {
547   my ($self, $source_name) = @_;
548   return sub {
549     my ($self, $version) = @_;
550     my $dirname = dir( $self->script_directory, '_source', 'deploy', $version );
551     $dirname->mkpath unless -d $dirname;
552
553     return "" . file( $dirname, "001-auto-$source_name.yml" );
554   }
555 }
556
557 sub install_resultsource {
558   my ($self, $args) = @_;
559   my $source          = $args->{result_source}
560     or die 'result_source must be passed to install_resultsource';
561   my $version         = $args->{version}
562     or die 'version must be passed to install_resultsource';
563   log_info { 'installing_resultsource ' . $source->source_name . ", version $version" };
564   my $rs_install_file =
565     $self->_resultsource_install_filename($source->source_name);
566
567   my $files = [
568      $self->$rs_install_file(
569       $self->storage->sqlt_type,
570       $version,
571     )
572   ];
573   $self->_run_sql_and_perl($files, '', [$version]);
574 }
575
576 sub prepare_resultsource_install {
577   my $self = shift;
578   my $source = (shift @_)->{result_source};
579   log_info { 'preparing install for resultsource ' . $source->source_name };
580
581   my $install_filename = $self->_resultsource_install_filename($source->source_name);
582   my $proto_filename = $self->_resultsource_protoschema_filename($source->source_name);
583   $self->prepare_protoschema({
584       parser_args => { sources => [$source->source_name], }
585   }, $proto_filename);
586   $self->_prepare_install({}, $proto_filename, $install_filename);
587 }
588
589 sub prepare_deploy {
590   log_info { 'preparing deploy' };
591   my $self = shift;
592   $self->prepare_protoschema({
593       # Exclude __VERSION so that it gets installed separately
594       parser_args => {
595          sources => [
596             sort { $a cmp $b }
597             grep { $_ ne '__VERSION' }
598             $self->schema->sources
599          ],
600       }
601   }, '_ddl_protoschema_produce_filename');
602   $self->_prepare_install({}, '_ddl_protoschema_produce_filename', '_ddl_schema_produce_filename');
603 }
604
605 sub prepare_upgrade {
606   my ($self, $args) = @_;
607   log_info {
608      "preparing upgrade from $args->{from_version} to $args->{to_version}"
609   };
610   $self->_prepare_changegrade(
611     $args->{from_version}, $args->{to_version}, $args->{version_set}, 'upgrade'
612   );
613 }
614
615 sub prepare_downgrade {
616   my ($self, $args) = @_;
617   log_info {
618      "preparing downgrade from $args->{from_version} to $args->{to_version}"
619   };
620   $self->_prepare_changegrade(
621     $args->{from_version}, $args->{to_version}, $args->{version_set}, 'downgrade'
622   );
623 }
624
625 sub _coderefs_per_files {
626   my ($self, $files) = @_;
627   no warnings 'redefine';
628   [map eval do { local( @ARGV, $/ ) = $_; <> }, @$files]
629 }
630
631 sub _prepare_changegrade {
632   my ($self, $from_version, $to_version, $version_set, $direction) = @_;
633   my $schema    = $self->schema;
634   my $databases = $self->databases;
635   my $dir       = $self->script_directory;
636
637   my $schema_version = $self->schema_version;
638   my $diff_file_method = "_ddl_schema_${direction}_produce_filename";
639   foreach my $db (@$databases) {
640     my $diff_file = $self->$diff_file_method($db, $version_set, $dir );
641     if(-e $diff_file) {
642       if ($self->force_overwrite) {
643          carp("Overwriting existing $direction-diff file - $diff_file");
644          unlink $diff_file;
645       } else {
646          die "Cannot overwrite '$diff_file', either enable force_overwrite or delete it"
647       }
648     }
649
650     open my $file, q(>), $diff_file;
651     print {$file} join ";\n", @{$self->_sqldiff_from_yaml($from_version, $to_version, $db, $direction)};
652     close $file;
653   }
654 }
655
656 sub _read_sql_file {
657   my ($self, $file)  = @_;
658   return unless $file;
659
660    local $/ = undef;  #sluuuuuurp
661
662   open my $fh, '<', $file;
663   return [ _split_sql_chunk( <$fh> ) ];
664 }
665
666 sub downgrade_single_step {
667   my $self = shift;
668   my $version_set = (shift @_)->{version_set};
669   Dlog_info { "downgrade_single_step'ing $_" } $version_set;
670
671   my $sqlt_type = $self->storage->sqlt_type;
672   my $sql_to_run;
673   if ($self->ignore_ddl) {
674      $sql_to_run = $self->_sqldiff_from_yaml(
675        $version_set->[0], $version_set->[1], $sqlt_type, 'downgrade',
676      );
677   }
678   my $sql = $self->_run_sql_and_perl($self->_ddl_schema_downgrade_consume_filenames(
679     $sqlt_type,
680     $version_set,
681   ), $sql_to_run, $version_set);
682
683   return ['', $sql];
684 }
685
686 sub upgrade_single_step {
687   my $self = shift;
688   my $version_set = (shift @_)->{version_set};
689   Dlog_info { "upgrade_single_step'ing $_" } $version_set;
690
691   my $sqlt_type = $self->storage->sqlt_type;
692   my $sql_to_run;
693   if ($self->ignore_ddl) {
694      $sql_to_run = $self->_sqldiff_from_yaml(
695        $version_set->[0], $version_set->[1], $sqlt_type, 'upgrade',
696      );
697   }
698   my $sql = $self->_run_sql_and_perl($self->_ddl_schema_upgrade_consume_filenames(
699     $sqlt_type,
700     $version_set,
701   ), $sql_to_run, $version_set);
702   return ['', $sql];
703 }
704
705 sub prepare_protoschema {
706   my $self      = shift;
707   my $sqltargs  = { %{$self->sql_translator_args}, %{shift @_} };
708   my $to_file   = shift;
709   my $filename
710     = $self->$to_file($self->schema_version);
711
712   # we do this because the code that uses this sets parser args,
713   # so we just need to merge in the package
714   my $sqlt = SQL::Translator->new({
715     parser                  => 'SQL::Translator::Parser::DBIx::Class',
716     producer                => 'SQL::Translator::Producer::YAML',
717     %{ $sqltargs },
718   });
719
720   my $yml = $sqlt->translate(data => $self->schema);
721
722   croak("Failed to translate to YAML: " . $sqlt->error)
723     unless $yml;
724
725   if (-e $filename ) {
726     if ($self->force_overwrite) {
727        carp "Overwriting existing DDL-YML file - $filename";
728        unlink $filename;
729     } else {
730        die "Cannot overwrite '$filename', either enable force_overwrite or delete it"
731     }
732   }
733
734   open my $file, q(>), $filename;
735   print {$file} $yml;
736   close $file;
737 }
738
739 __PACKAGE__->meta->make_immutable;
740
741 1;
742
743 # vim: ts=2 sw=2 expandtab
744
745 __END__
746
747 =head1 DESCRIPTION
748
749 This class is the meat of L<DBIx::Class::DeploymentHandler>.  It takes care
750 of generating serialized schemata  as well as sql files to move from one
751 version of a schema to the rest.  One of the hallmark features of this class
752 is that it allows for multiple sql files for deploy and upgrade, allowing
753 developers to fine tune deployment.  In addition it also allows for perl
754 files to be run at any stage of the process.
755
756 For basic usage see L<DBIx::Class::DeploymentHandler::HandlesDeploy>.  What's
757 documented here is extra fun stuff or private methods.
758
759 =head1 DIRECTORY LAYOUT
760
761 Arguably this is the best feature of L<DBIx::Class::DeploymentHandler>.
762 It's spiritually based upon L<DBIx::Migration::Directories>, but has a
763 lot of extensions and modifications, so even if you are familiar with it,
764 please read this.  I feel like the best way to describe the layout is with
765 the following example:
766
767  $sql_migration_dir
768  |- _source
769  |  |- deploy
770  |     |- 1
771  |     |  `- 001-auto.yml
772  |     |- 2
773  |     |  `- 001-auto.yml
774  |     `- 3
775  |        `- 001-auto.yml
776  |- SQLite
777  |  |- downgrade
778  |  |  `- 2-1
779  |  |     `- 001-auto.sql
780  |  |- deploy
781  |  |  `- 1
782  |  |     `- 001-auto.sql
783  |  `- upgrade
784  |     |- 1-2
785  |     |  `- 001-auto.sql
786  |     `- 2-3
787  |        `- 001-auto.sql
788  |- _common
789  |  |- downgrade
790  |  |  `- 2-1
791  |  |     `- 002-remove-customers.pl
792  |  `- upgrade
793  |     `- 1-2
794  |     |  `- 002-generate-customers.pl
795  |     `- _any
796  |        `- 999-bump-action.pl
797  `- MySQL
798     |- downgrade
799     |  `- 2-1
800     |     `- 001-auto.sql
801     |- initialize
802     |  `- 1
803     |     |- 001-create_database.pl
804     |     `- 002-create_users_and_permissions.pl
805     |- deploy
806     |  `- 1
807     |     `- 001-auto.sql
808     `- upgrade
809        `- 1-2
810           `- 001-auto.sql
811
812 So basically, the code
813
814  $dm->deploy(1)
815
816 on an C<SQLite> database that would simply run
817 C<$sql_migration_dir/SQLite/deploy/1/001-auto.sql>.  Next,
818
819  $dm->upgrade_single_step([1,2])
820
821 would run C<$sql_migration_dir/SQLite/upgrade/1-2/001-auto.sql> followed by
822 C<$sql_migration_dir/_common/upgrade/1-2/002-generate-customers.pl>, and
823 finally punctuated by
824 C<$sql_migration_dir/_common/upgrade/_any/999-bump-action.pl>.
825
826 C<.pl> files don't have to be in the C<_common> directory, but most of the time
827 they should be, because perl scripts are generally database independent.
828
829 Note that unlike most steps in the process, C<initialize> will not run SQL, as
830 there may not even be an database at initialize time.  It will run perl scripts
831 just like the other steps in the process, but nothing is passed to them.
832 Until people have used this more it will remain freeform, but a recommended use
833 of initialize is to have it prompt for username and password, and then call the
834 appropriate C<< CREATE DATABASE >> commands etc.
835
836 =head2 Directory Specification
837
838 The following subdirectories are recognized by this DeployMethod:
839
840 =over 2
841
842 =item C<_source> This directory can contain the following directories:
843
844 =over 2
845
846 =item C<deploy> This directory merely contains directories named after schema
847 versions, which in turn contain C<yaml> files that are serialized versions
848 of the schema at that version.  These files are not for editing by hand.
849
850 =back
851
852 =item C<_preprocess_schema> This directory can contain the following
853 directories:
854
855 =over 2
856
857 =item C<downgrade> This directory merely contains directories named after
858 migrations, which are of the form C<$from_version-$to_version>.  Inside of
859 these directories you may put Perl scripts which are to return a subref
860 that takes the arguments C<< $from_schema, $to_schema >>, which are
861 L<SQL::Translator::Schema> objects.
862
863 =item C<upgrade> This directory merely contains directories named after
864 migrations, which are of the form C<$from_version-$to_version>.  Inside of
865 these directories you may put Perl scripts which are to return a subref
866 that takes the arguments C<< $from_schema, $to_schema >>, which are
867 L<SQL::Translator::Schema> objects.
868
869 =back
870
871 =item C<$storage_type> This is a set of scripts that gets run depending on what
872 your storage type is.  If you are not sure what your storage type is, take a
873 look at the producers listed for L<SQL::Translator>.  Also note, C<_common>
874 is a special case.  C<_common> will get merged into whatever other files you
875 already have.  This directory can contain the following directories itself:
876
877 =over 2
878
879 =item C<initialize> Gets run before the C<deploy> is C<deploy>ed.  Has the
880 same structure as the C<deploy> subdirectory as well; that is, it has a
881 directory for each schema version.  Unlike C<deploy>, C<upgrade>, and C<downgrade>
882 though, it can only run C<.pl> files, and the coderef in the perl files get
883 no arguments passed to them.
884
885 =item C<deploy> Gets run when the schema is C<deploy>ed.  Structure is a
886 directory per schema version, and then files are merged with C<_common> and run
887 in filename order.  C<.sql> files are merely run, as expected.  C<.pl> files are
888 run according to L</PERL SCRIPTS>.
889
890 =item C<upgrade> Gets run when the schema is C<upgrade>d.  Structure is a directory
891 per upgrade step, (for example, C<1-2> for upgrading from version 1 to version
892 2,) and then files are merged with C<_common> and run in filename order.
893 C<.sql> files are merely run, as expected.  C<.pl> files are run according
894 to L</PERL SCRIPTS>.
895
896 =item C<downgrade> Gets run when the schema is C<downgrade>d.  Structure is a directory
897 per downgrade step, (for example, C<2-1> for downgrading from version 2 to version
898 1,) and then files are merged with C<_common> and run in filename order.
899 C<.sql> files are merely run, as expected.  C<.pl> files are run according
900 to L</PERL SCRIPTS>.
901
902
903 =back
904
905 =back
906
907 Note that there can be an C<_any> in the place of any of the versions (like
908 C<1-2> or C<1>), which means those scripts will be run B<every> time.  So if
909 you have an C<_any> in C<_common/upgrade>, that script will get run for every
910 upgrade.
911
912 =head1 PERL SCRIPTS
913
914 A perl script for this tool is very simple.  It merely needs to contain an
915 anonymous sub that takes a L<DBIx::Class::Schema> and the version set as it's
916 arguments.
917
918 A very basic perl script might look like:
919
920  #!perl
921
922  use strict;
923  use warnings;
924
925  use DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator::ScriptHelpers
926     'schema_from_schema_loader';
927
928  schema_from_schema_loader({ naming => 'v4' }, sub {
929    my $schema = shift;
930
931    # [1] for deploy, [1,2] for upgrade or downgrade, probably used with _any
932    my $versions = shift;
933
934    $schema->resultset('Users')->create({
935      name => 'root',
936      password => 'root',
937    })
938  })
939
940 Note that the above uses
941 L<DBIx::Class::DeploymentHanlder::DeployMethod::SQL::Translator::ScriptHelpers/schema_from_schema_loader>.
942 Using a raw coderef is strongly discouraged as it is likely to break as you
943 modify your schema.
944
945 =attr ignore_ddl
946
947 This attribute will, when set to true (default is false), cause the DM to use
948 L<SQL::Translator> to use the C<_source>'s serialized SQL::Translator::Schema
949 instead of any pregenerated SQL.  If you have a development server this is
950 probably the best plan of action as you will not be putting as many generated
951 files in your version control.  Goes well with with C<databases> of C<[]>.
952
953 =attr force_overwrite
954
955 When this attribute is true generated files will be overwritten when the
956 methods which create such files are run again.  The default is false, in which
957 case the program will die with a message saying which file needs to be deleted.
958
959 =attr schema
960
961 The L<DBIx::Class::Schema> (B<required>) that is used to talk to the database
962 and generate the DDL.
963
964 =attr storage
965
966 The L<DBIx::Class::Storage> that is I<actually> used to talk to the database
967 and generate the DDL.  This is automatically created with L</_build_storage>.
968
969 =attr sql_translator_args
970
971 The arguments that get passed to L<SQL::Translator> when it's used.
972
973 =attr script_directory
974
975 The directory (default C<'sql'>) that scripts are stored in
976
977 =attr databases
978
979 The types of databases (default C<< [qw( MySQL SQLite PostgreSQL )] >>) to
980 generate files for
981
982 =attr txn_wrap
983
984 Set to true (which is the default) to wrap all upgrades and deploys in a single
985 transaction.
986
987 =attr schema_version
988
989 The version the schema on your harddrive is at.  Defaults to
990 C<< $self->schema->schema_version >>.
991
992 =head1 SEE ALSO
993
994 This class is an implementation of
995 L<DBIx::Class::DeploymentHandler::HandlesDeploy>.  Pretty much all the
996 documentation is there.