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