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