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