nicer logging
[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), -default_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 schema => (
29   isa      => 'DBIx::Class::Schema',
30   is       => 'ro',
31   required => 1,
32 );
33
34 has storage => (
35   isa        => 'DBIx::Class::Storage',
36   is         => 'ro',
37   lazy_build => 1,
38 );
39
40 method _build_storage {
41   my $s = $self->schema->storage;
42   $s->_determine_driver;
43   $s
44 }
45
46 has sql_translator_args => (
47   isa => 'HashRef',
48   is  => 'ro',
49   default => sub { {} },
50 );
51 has script_directory => (
52   isa      => 'Str',
53   is       => 'ro',
54   required => 1,
55   default  => 'sql',
56 );
57
58 has databases => (
59   coerce  => 1,
60   isa     => 'DBIx::Class::DeploymentHandler::Databases',
61   is      => 'ro',
62   default => sub { [qw( MySQL SQLite PostgreSQL )] },
63 );
64
65 has txn_wrap => (
66   is => 'ro',
67   isa => 'Bool',
68   default => 1,
69 );
70
71 has schema_version => (
72   is => 'ro',
73   isa => 'Str',
74   lazy_build => 1,
75 );
76
77 # this will probably never get called as the DBICDH
78 # will be passing down a schema_version normally, which
79 # is built the same way, but we leave this in place
80 method _build_schema_version { $self->schema->schema_version }
81
82 has _json => (
83   is => 'ro',
84   lazy_build => 1,
85 );
86
87 sub _build__json { require JSON; JSON->new->pretty }
88
89 method __ddl_consume_with_prefix($type, $versions, $prefix) {
90   my $base_dir = $self->script_directory;
91
92   my $main    = catfile( $base_dir, $type      );
93   my $generic = catfile( $base_dir, '_generic' );
94   my $common  =
95     catfile( $base_dir, '_common', $prefix, join q(-), @{$versions} );
96
97   my $dir;
98   if (-d $main) {
99     $dir = catfile($main, $prefix, join q(-), @{$versions})
100   } elsif (-d $generic) {
101     $dir = catfile($generic, $prefix, join q(-), @{$versions});
102   } else {
103     croak "neither $main or $generic exist; please write/generate some SQL";
104   }
105
106   opendir my($dh), $dir;
107   my %files = map { $_ => "$dir/$_" } grep { /\.(?:sql|pl|sql-\w+)$/ && -f "$dir/$_" } readdir $dh;
108   closedir $dh;
109
110   if (-d $common) {
111     opendir my($dh), $common;
112     for my $filename (grep { /\.(?:sql|pl)$/ && -f catfile($common,$_) } readdir $dh) {
113       unless ($files{$filename}) {
114         $files{$filename} = catfile($common,$filename);
115       }
116     }
117     closedir $dh;
118   }
119
120   return [@files{sort keys %files}]
121 }
122
123 method _ddl_preinstall_consume_filenames($type, $version) {
124   $self->__ddl_consume_with_prefix($type, [ $version ], 'preinstall')
125 }
126
127 method _ddl_schema_consume_filenames($type, $version) {
128   $self->__ddl_consume_with_prefix($type, [ $version ], 'schema')
129 }
130
131 method _ddl_schema_produce_filename($type, $version) {
132   my $dirname = catfile( $self->script_directory, $type, 'schema', $version );
133   mkpath($dirname) unless -d $dirname;
134
135   return catfile( $dirname, '001-auto.sql-json' );
136 }
137
138 method _ddl_schema_up_consume_filenames($type, $versions) {
139   $self->__ddl_consume_with_prefix($type, $versions, 'up')
140 }
141
142 method _ddl_schema_down_consume_filenames($type, $versions) {
143   $self->__ddl_consume_with_prefix($type, $versions, 'down')
144 }
145
146 method _ddl_schema_up_produce_filename($type, $versions) {
147   my $dir = $self->script_directory;
148
149   my $dirname = catfile( $dir, $type, 'up', join q(-), @{$versions});
150   mkpath($dirname) unless -d $dirname;
151
152   return catfile( $dirname, '001-auto.sql-json' );
153 }
154
155 method _ddl_schema_down_produce_filename($type, $versions, $dir) {
156   my $dirname = catfile( $dir, $type, 'down', join q(-), @{$versions} );
157   mkpath($dirname) unless -d $dirname;
158
159   return catfile( $dirname, '001-auto.sql-json');
160 }
161
162 method _run_sql_array($sql) {
163   my $storage = $self->storage;
164
165   $sql = [grep {
166     $_ && # remove blank lines
167     !/^(BEGIN|BEGIN TRANSACTION|COMMIT)/ # strip txn's
168   } map {
169     s/^\s+//; s/\s+$//; # trim whitespace
170     join '', grep { !/^--/ } split /\n/ # remove comments
171   } @$sql];
172
173   Dlog_trace { "[DBICDH] Running SQL $_" } $sql;
174   foreach my $line (@{$sql}) {
175     $storage->_query_start($line);
176     # the whole reason we do this is so that we can see the line that was run
177     try {
178       $storage->dbh_do (sub { $_[1]->do($line) });
179     }
180     catch {
181       die "$_ (running line '$line')"
182     }
183     $storage->_query_end($line);
184   }
185   return join "\n", @$sql
186 }
187
188 method _run_sql($filename) {
189   log_debug { "[DBICDH] Running SQL from $filename" };
190   return $self->_run_sql_array($self->_read_sql_file($filename));
191 }
192
193 method _run_perl($filename) {
194   log_debug { "[DBICDH] Running Perl from $filename" };
195   my $filedata = do { local( @ARGV, $/ ) = $filename; <> };
196
197   no warnings 'redefine';
198   my $fn = eval "$filedata";
199   use warnings;
200   Dlog_trace { "[DBICDH] Running Perl $_" } $fn;
201
202   if ($@) {
203     carp "$filename failed to compile: $@";
204   } elsif (ref $fn eq 'CODE') {
205     $fn->($self->schema)
206   } else {
207     carp "$filename should define an anonymouse sub that takes a schema but it didn't!";
208   }
209 }
210
211 method _run_serialized_sql($filename, $type) {
212   if (lc $type eq 'json') {
213     return $self->_run_sql_array($self->_json->decode(
214       do { local( @ARGV, $/ ) = $filename; <> } # slurp
215     ))
216   } else {
217     croak "$type is not one of the supported serialzed types"
218   }
219 }
220
221 method _run_sql_and_perl($filenames) {
222   my @files   = @{$filenames};
223   my $guard   = $self->schema->txn_scope_guard if $self->txn_wrap;
224
225   my $sql = '';
226   for my $filename (@files) {
227     if ($filename =~ /\.sql$/) {
228        $sql .= $self->_run_sql($filename)
229     } elsif ( $filename =~ /\.sql-(\w+)$/ ) {
230        $sql .= $self->_run_serialized_sql($filename, $1)
231     } elsif ( $filename =~ /\.pl$/ ) {
232        $self->_run_perl($filename)
233     } else {
234       croak "A file ($filename) got to deploy that wasn't sql or perl!";
235     }
236   }
237
238   $guard->commit if $self->txn_wrap;
239
240   return $sql;
241 }
242
243 sub deploy {
244   my $self = shift;
245   my $version = (shift @_ || {})->{version} || $self->schema_version;
246   log_info { "[DBICDH] deploying version $version" };
247
248   return $self->_run_sql_and_perl($self->_ddl_schema_consume_filenames(
249     $self->storage->sqlt_type,
250     $version,
251   ));
252 }
253
254 sub preinstall {
255   my $self         = shift;
256   my $args         = shift;
257   my $version      = $args->{version}      || $self->schema_version;
258   log_info { "[DBICDH] preinstalling version $version" };
259   my $storage_type = $args->{storage_type} || $self->storage->sqlt_type;
260
261   my @files = @{$self->_ddl_preinstall_consume_filenames(
262     $storage_type,
263     $version,
264   )};
265
266   for my $filename (@files) {
267     # We ignore sql for now (till I figure out what to do with it)
268     if ( $filename =~ /^(.+)\.pl$/ ) {
269       my $filedata = do { local( @ARGV, $/ ) = $filename; <> };
270
271       no warnings 'redefine';
272       my $fn = eval "$filedata";
273       use warnings;
274
275       if ($@) {
276         carp "$filename failed to compile: $@";
277       } elsif (ref $fn eq 'CODE') {
278         $fn->()
279       } else {
280         carp "$filename should define an anonymous sub but it didn't!";
281       }
282     } else {
283       croak "A file ($filename) got to preinstall_scripts that wasn't sql or perl!";
284     }
285   }
286 }
287
288 sub _prepare_install {
289   my $self      = shift;
290   my $sqltargs  = { %{$self->sql_translator_args}, %{shift @_} };
291   my $to_file   = shift;
292   my $schema    = $self->schema;
293   my $databases = $self->databases;
294   my $dir       = $self->script_directory;
295   my $version   = $self->schema_version;
296
297   my $sqlt = SQL::Translator->new({
298     no_comments             => 1,
299     add_drop_table          => 1,
300     ignore_constraint_names => 1,
301     ignore_index_names      => 1,
302     parser                  => 'SQL::Translator::Parser::DBIx::Class',
303     %{$sqltargs}
304   });
305
306   my $sqlt_schema = $sqlt->translate( data => $schema )
307     or croak($sqlt->error);
308
309   foreach my $db (@$databases) {
310     $sqlt->reset;
311     $sqlt->{schema} = $sqlt_schema;
312     $sqlt->producer($db);
313
314     my $filename = $self->$to_file($db, $version, $dir);
315     if (-e $filename ) {
316       carp "Overwriting existing DDL file - $filename";
317       unlink $filename;
318     }
319
320     my $sql = $self->_generate_final_sql($sqlt);
321     if(!$sql) {
322       carp("Failed to translate to $db, skipping. (" . $sqlt->error . ")");
323       next;
324     }
325     open my $file, q(>), $filename;
326     print {$file} $sql;
327     close $file;
328   }
329 }
330
331 method _generate_final_sql($sqlt) {
332   my @output = $sqlt->translate;
333   $self->_json->encode(\@output);
334 }
335
336 sub _resultsource_install_filename {
337   my ($self, $source_name) = @_;
338   return sub {
339     my ($self, $type, $version) = @_;
340     my $dirname = catfile( $self->script_directory, $type, 'schema', $version );
341     mkpath($dirname) unless -d $dirname;
342
343     return catfile( $dirname, "001-auto-$source_name.sql-json" );
344   }
345 }
346
347 sub install_resultsource {
348   my ($self, $args) = @_;
349   my $source          = $args->{result_source};
350   my $version         = $args->{version};
351   log_info { '[DBICDH] installing_resultsource ' . $source->source_name . ", version $version" };
352   my $rs_install_file =
353     $self->_resultsource_install_filename($source->source_name);
354
355   my $files = [
356      $self->$rs_install_file(
357       $self->storage->sqlt_type,
358       $version,
359     )
360   ];
361   $self->_run_sql_and_perl($files);
362 }
363
364 sub prepare_resultsource_install {
365   my $self = shift;
366   my $source = (shift @_)->{result_source};
367   log_info { '[DBICDH] preparing install for resultsource ' . $source->source_name };
368
369   my $filename = $self->_resultsource_install_filename($source->source_name);
370   $self->_prepare_install({
371       parser_args => { sources => [$source->source_name], }
372     }, $filename);
373 }
374
375 sub prepare_deploy {
376   log_info { '[DBICDH] preparing deploy' };
377   my $self = shift;
378   $self->_prepare_install({}, '_ddl_schema_produce_filename');
379 }
380
381 sub prepare_upgrade {
382   my ($self, $args) = @_;
383   log_info {
384      '[DBICDH] preparing upgrade ' .
385      "from $args->{from_version} to $args->{to_version}"
386   };
387   $self->_prepare_changegrade(
388     $args->{from_version}, $args->{to_version}, $args->{version_set}, 'up'
389   );
390 }
391
392 sub prepare_downgrade {
393   my ($self, $args) = @_;
394   log_info {
395      '[DBICDH] preparing downgrade ' .
396      "from $args->{from_version} to $args->{to_version}"
397   };
398   $self->_prepare_changegrade(
399     $args->{from_version}, $args->{to_version}, $args->{version_set}, 'down'
400   );
401 }
402
403 method _prepare_changegrade($from_version, $to_version, $version_set, $direction) {
404   my $schema    = $self->schema;
405   my $databases = $self->databases;
406   my $dir       = $self->script_directory;
407   my $sqltargs  = $self->sql_translator_args;
408
409   my $schema_version = $self->schema_version;
410
411   $sqltargs = {
412     add_drop_table => 1,
413     no_comments => 1,
414     ignore_constraint_names => 1,
415     ignore_index_names => 1,
416     %{$sqltargs}
417   };
418
419   my $sqlt = SQL::Translator->new( $sqltargs );
420
421   $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
422   my $sqlt_schema = $sqlt->translate( data => $schema )
423     or croak($sqlt->error);
424
425   foreach my $db (@$databases) {
426     $sqlt->reset;
427     $sqlt->{schema} = $sqlt_schema;
428     $sqlt->producer($db);
429
430     my $prefilename = $self->_ddl_schema_produce_filename($db, $from_version, $dir);
431     unless(-e $prefilename) {
432       carp("No previous schema file found ($prefilename)");
433       next;
434     }
435     my $diff_file_method = "_ddl_schema_${direction}_produce_filename";
436     my $diff_file = $self->$diff_file_method($db, $version_set, $dir );
437     if(-e $diff_file) {
438       carp("Overwriting existing $direction-diff file - $diff_file");
439       unlink $diff_file;
440     }
441
442     my $source_schema;
443     {
444       my $t = SQL::Translator->new({
445          %{$sqltargs},
446          debug => 0,
447          trace => 0,
448       });
449
450       $t->parser( $db ) # could this really throw an exception?
451         or croak($t->error);
452
453       my $sql = $self->_default_read_sql_file_as_string($prefilename);
454       my $out = $t->translate( \$sql )
455         or croak($t->error);
456
457       $source_schema = $t->schema;
458
459       $source_schema->name( $prefilename )
460         unless  $source_schema->name;
461     }
462
463     # The "new" style of producers have sane normalization and can support
464     # diffing a SQL file against a DBIC->SQLT schema. Old style ones don't
465     # And we have to diff parsed SQL against parsed SQL.
466     my $dest_schema = $sqlt_schema;
467
468     unless ( "SQL::Translator::Producer::$db"->can('preprocess_schema') ) {
469       my $t = SQL::Translator->new({
470          %{$sqltargs},
471          debug => 0,
472          trace => 0,
473       });
474
475       $t->parser( $db ) # could this really throw an exception?
476         or croak($t->error);
477
478       my $filename = $self->_ddl_schema_produce_filename($db, $to_version, $dir);
479       my $sql = $self->_default_read_sql_file_as_string($filename);
480       my $out = $t->translate( \$sql )
481         or croak($t->error);
482
483       $dest_schema = $t->schema;
484
485       $dest_schema->name( $filename )
486         unless $dest_schema->name;
487     }
488
489     open my $file, q(>), $diff_file;
490     print {$file}
491       $self->_generate_final_diff($source_schema, $dest_schema, $db, $sqltargs);
492     close $file;
493   }
494 }
495
496 method _generate_final_diff($source_schema, $dest_schema, $db, $sqltargs) {
497   $self->_json->encode([
498      SQL::Translator::Diff::schema_diff(
499         $source_schema, $db,
500         $dest_schema,   $db,
501         $sqltargs
502      )
503   ])
504 }
505
506 method _read_sql_file($file) {
507   return unless $file;
508
509   open my $fh, '<', $file;
510   my @data = split /;\n/, join '', <$fh>;
511   close $fh;
512
513   return \@data;
514 }
515
516 method _default_read_sql_file_as_string($file) {
517   return join q(), map "$_;\n", @{$self->_json->decode(
518     do { local( @ARGV, $/ ) = $file; <> } # slurp
519   )};
520 }
521
522 sub downgrade_single_step {
523   my $self = shift;
524   my $version_set = (shift @_)->{version_set};
525   Dlog_info { qq([DBICDH] downgrade_single_step'ing $_) } $version_set;
526
527   my $sql = $self->_run_sql_and_perl($self->_ddl_schema_down_consume_filenames(
528     $self->storage->sqlt_type,
529     $version_set,
530   ));
531
532   return ['', $sql];
533 }
534
535 sub upgrade_single_step {
536   my $self = shift;
537   my $version_set = (shift @_)->{version_set};
538   Dlog_info { qq([DBICDH] upgrade_single_step'ing $_) } $version_set;
539
540   my $sql = $self->_run_sql_and_perl($self->_ddl_schema_up_consume_filenames(
541     $self->storage->sqlt_type,
542     $version_set,
543   ));
544   return ['', $sql];
545 }
546
547 __PACKAGE__->meta->make_immutable;
548
549 1;
550
551 # vim: ts=2 sw=2 expandtab
552
553 __END__
554
555 =head1 DESCRIPTION
556
557 This class is the meat of L<DBIx::Class::DeploymentHandler>.  It takes
558 care of generating serialized sql files representing schemata as well
559 as serialized sql files to move from one version of a schema to the rest.
560 One of the hallmark features of this class is that it allows for multiple sql
561 files for deploy and upgrade, allowing developers to fine tune deployment.
562 In addition it also allows for perl files to be run
563 at any stage of the process.
564
565 For basic usage see L<DBIx::Class::DeploymentHandler::HandlesDeploy>.  What's
566 documented here is extra fun stuff or private methods.
567
568 =head1 DIRECTORY LAYOUT
569
570 Arguably this is the best feature of L<DBIx::Class::DeploymentHandler>.  It's
571 heavily based upon L<DBIx::Migration::Directories>, but has some extensions and
572 modifications, so even if you are familiar with it, please read this.  I feel
573 like the best way to describe the layout is with the following example:
574
575  $sql_migration_dir
576  |- SQLite
577  |  |- down
578  |  |  `- 2-1
579  |  |     `- 001-auto.sql-json
580  |  |- schema
581  |  |  `- 1
582  |  |     `- 001-auto.sql-json
583  |  `- up
584  |     |- 1-2
585  |     |  `- 001-auto.sql-json
586  |     `- 2-3
587  |        `- 001-auto.sql-json
588  |- _common
589  |  |- down
590  |  |  `- 2-1
591  |  |     `- 002-remove-customers.pl
592  |  `- up
593  |     `- 1-2
594  |        `- 002-generate-customers.pl
595  |- _generic
596  |  |- down
597  |  |  `- 2-1
598  |  |     `- 001-auto.sql-json
599  |  |- schema
600  |  |  `- 1
601  |  |     `- 001-auto.sql-json
602  |  `- up
603  |     `- 1-2
604  |        |- 001-auto.sql-json
605  |        `- 002-create-stored-procedures.sql
606  `- MySQL
607     |- down
608     |  `- 2-1
609     |     `- 001-auto.sql-json
610     |- preinstall
611     |  `- 1
612     |     |- 001-create_database.pl
613     |     `- 002-create_users_and_permissions.pl
614     |- schema
615     |  `- 1
616     |     `- 001-auto.sql-json
617     `- up
618        `- 1-2
619           `- 001-auto.sql-json
620
621 So basically, the code
622
623  $dm->deploy(1)
624
625 on an C<SQLite> database that would simply run
626 C<$sql_migration_dir/SQLite/schema/1/001-auto.sql-json>.  Next,
627
628  $dm->upgrade_single_step([1,2])
629
630 would run C<$sql_migration_dir/SQLite/up/1-2/001-auto.sql-json> followed by
631 C<$sql_migration_dir/_common/up/1-2/002-generate-customers.pl>.
632
633 C<.pl> files don't have to be in the C<_common> directory, but most of the time
634 they should be, because perl scripts are generally be database independent.
635
636 C<_generic> exists for when you for some reason are sure that your SQL is
637 generic enough to run on all databases.  Good luck with that one.
638
639 Note that unlike most steps in the process, C<preinstall> will not run SQL, as
640 there may not even be an database at preinstall time.  It will run perl scripts
641 just like the other steps in the process, but nothing is passed to them.
642 Until people have used this more it will remain freeform, but a recommended use
643 of preinstall is to have it prompt for username and password, and then call the
644 appropriate C<< CREATE DATABASE >> commands etc.
645
646 =head1 SERIALIZED SQL
647
648 The SQL that this module generates and uses is serialized into an array of
649 SQL statements.  The reason being that some databases handle multiple
650 statements in a single execution differently.  Generally you do not need to
651 worry about this as these are scripts generated for you.  If you find that
652 you are editing them on a regular basis something is wrong and you either need
653 to submit a bug or consider writing extra serialized SQL or Perl scripts to run
654 before or after the automatically generated script.
655
656 B<NOTE:> Currently the SQL is serialized into JSON.  I am willing to merge in
657 patches that will allow more serialization formats if you want that feature,
658 but if you do send me a patch for that realize that I do not want to add YAML
659 support or whatever, I would rather add a generic method of adding any
660 serialization format.
661
662 =head1 PERL SCRIPTS
663
664 A perl script for this tool is very simple.  It merely needs to contain an
665 anonymous sub that takes a L<DBIx::Class::Schema> as it's only argument.
666 A very basic perl script might look like:
667
668  #!perl
669
670  use strict;
671  use warnings;
672
673  sub {
674    my $schema = shift;
675
676    $schema->resultset('Users')->create({
677      name => 'root',
678      password => 'root',
679    })
680  }
681
682 =attr schema
683
684 The L<DBIx::Class::Schema> (B<required>) that is used to talk to the database
685 and generate the DDL.
686
687 =attr storage
688
689 The L<DBIx::Class::Storage> that is I<actually> used to talk to the database
690 and generate the DDL.  This is automatically created with L</_build_storage>.
691
692 =attr sql_translator_args
693
694 The arguments that get passed to L<SQL::Translator> when it's used.
695
696 =attr script_directory
697
698 The directory (default C<'sql'>) that scripts are stored in
699
700 =attr databases
701
702 The types of databases (default C<< [qw( MySQL SQLite PostgreSQL )] >>) to
703 generate files for
704
705 =attr txn_wrap
706
707 Set to true (which is the default) to wrap all upgrades and deploys in a single
708 transaction.
709
710 =attr schema_version
711
712 The version the schema on your harddrive is at.  Defaults to
713 C<< $self->schema->schema_version >>.
714
715 =begin comment
716
717 =head2 __ddl_consume_with_prefix
718
719  $dm->__ddl_consume_with_prefix( 'SQLite', [qw( 1.00 1.01 )], 'up' )
720
721 This is the meat of the multi-file upgrade/deploy stuff.  It returns a list of
722 files in the order that they should be run for a generic "type" of upgrade.
723 You should not be calling this in user code.
724
725 =head2 _ddl_schema_consume_filenames
726
727  $dm->__ddl_schema_consume_filenames( 'SQLite', [qw( 1.00 )] )
728
729 Just a curried L</__ddl_consume_with_prefix>.  Get's a list of files for an
730 initial deploy.
731
732 =head2 _ddl_schema_produce_filename
733
734  $dm->__ddl_schema_produce_filename( 'SQLite', [qw( 1.00 )] )
735
736 Returns a single file in which an initial schema will be stored.
737
738 =head2 _ddl_schema_up_consume_filenames
739
740  $dm->_ddl_schema_up_consume_filenames( 'SQLite', [qw( 1.00 )] )
741
742 Just a curried L</__ddl_consume_with_prefix>.  Get's a list of files for an
743 upgrade.
744
745 =head2 _ddl_schema_down_consume_filenames
746
747  $dm->_ddl_schema_down_consume_filenames( 'SQLite', [qw( 1.00 )] )
748
749 Just a curried L</__ddl_consume_with_prefix>.  Get's a list of files for a
750 downgrade.
751
752 =head2 _ddl_schema_up_produce_filenames
753
754  $dm->_ddl_schema_up_produce_filename( 'SQLite', [qw( 1.00 1.01 )] )
755
756 Returns a single file in which the sql to upgrade from one schema to another
757 will be stored.
758
759 =head2 _ddl_schema_down_produce_filename
760
761  $dm->_ddl_schema_down_produce_filename( 'SQLite', [qw( 1.00 1.01 )] )
762
763 Returns a single file in which the sql to downgrade from one schema to another
764 will be stored.
765
766 =head2 _resultsource_install_filename
767
768  my $filename_fn = $dm->_resultsource_install_filename('User');
769  $dm->$filename_fn('SQLite', '1.00')
770
771 Returns a function which in turn returns a single filename used to install a
772 single resultsource.  Weird interface is convenient for me.  Deal with it.
773
774 =head2 _run_sql_and_perl
775
776  $dm->_run_sql_and_perl([qw( list of filenames )])
777
778 Simply put, this runs the list of files passed to it.  If the file ends in
779 C<.sql> it runs it as sql and if it ends in C<.pl> it runs it as a perl file.
780
781 Depending on L</txn_wrap> all of the files run will be wrapped in a single
782 transaction.
783
784 =head2 _prepare_install
785
786  $dm->_prepare_install({ add_drop_table => 0 }, sub { 'file_to_create' })
787
788 Generates the sql file for installing the database.  First arg is simply
789 L<SQL::Translator> args and the second is a coderef that returns the filename
790 to store the sql in.
791
792 =head2 _prepare_changegrade
793
794  $dm->_prepare_changegrade('1.00', '1.01', [qw( 1.00 1.01)], 'up')
795
796 Generates the sql file for migrating from one schema version to another.  First
797 arg is the version to start from, second is the version to go to, third is the
798 L<version set|DBIx::Class::DeploymentHandler/VERSION SET>, and last is the
799 direction of the changegrade, be it 'up' or 'down'.
800
801 =head2 _read_sql_file
802
803  $dm->_read_sql_file('foo.sql')
804
805 Reads a sql file and returns lines in an C<ArrayRef>.  Strips out comments,
806 transactions, and blank lines.
807
808 =end comment