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