1 package DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator;
3 use Method::Signatures::Simple;
6 require SQL::Translator::Diff;
7 require DBIx::Class::Storage; # loaded for type constraint
9 with 'DBIx::Class::DeploymentHandler::HandlesDeploy';
13 isa => 'DBIx::Class::Schema',
16 handles => [qw( schema_version )],
20 isa => 'DBIx::Class::Storage',
25 method _build_storage {
26 my $s = $self->schema->storage;
27 $s->_determine_driver;
34 default => sub { {} },
36 has upgrade_directory => (
45 isa => 'DBIx::Class::DeploymentHandler::Databases',
47 default => sub { [qw( MySQL SQLite PostgreSQL )] },
51 isa => 'ArrayRef[Str]',
55 # these two methods should go away once we switch to
56 # DBIx::Migration::Directories
57 method _ddl_schema_filename($type, $version, $dir) {
58 my $filename = ref $self->schema;
59 $filename =~ s/::/-/g;
61 $filename = File::Spec->catfile(
62 $dir, "$filename-schema-$version-$type.sql"
68 method _ddl_schema_diff_filename($type, $versions, $dir) {
69 my $filename = ref $self->schema;
70 $filename =~ s/::/-/g;
72 $filename = File::Spec->catfile(
73 $dir, "$filename-diff-" . join( q(-), @{$versions} ) . "-$type.sql"
79 method _deployment_statements {
80 my $dir = $self->upgrade_directory;
81 my $schema = $self->schema;
82 my $type = $self->storage->sqlt_type;
83 my $sqltargs = $self->sqltargs;
84 my $version = $self->schema_version;
86 my $filename = $self->_ddl_schema_filename($type, $version, $dir);
89 open $file, q(<), $filename
90 or carp "Can't open $filename ($!)";
93 return join '', @rows;
96 # sources needs to be a parser arg, but for simplicty allow at top level
98 $sqltargs->{parser_args}{sources} = delete $sqltargs->{sources}
99 if exists $sqltargs->{sources};
101 my $tr = SQL::Translator->new(
102 producer => "SQL::Translator::Producer::${type}",
104 parser => 'SQL::Translator::Parser::DBIx::Class',
111 @ret = $tr->translate;
114 $ret[0] = $tr->translate;
117 $schema->throw_exception( 'Unable to produce deployment statements: ' . $tr->error)
118 unless (@ret && defined $ret[0]);
120 return $wa ? @ret : $ret[0];
125 my $storage = $self->storage;
129 return if(!$line || $line =~ /^--|^BEGIN TRANSACTION|^COMMIT|^\s+$/);
130 $storage->_query_start($line);
132 # do a dbh_do cycle here, as we need some error checking in
133 # place (even though we will ignore errors)
134 $storage->dbh_do (sub { $_[1]->do($line) });
137 carp "$_ (running '${line}')"
139 $storage->_query_end($line);
141 my @statements = $self->_deployment_statements();
142 if (@statements > 1) {
143 foreach my $statement (@statements) {
144 $deploy->( $statement );
147 elsif (@statements == 1) {
148 foreach my $line ( split(";\n", $statements[0])) {
154 sub prepare_install {
156 my $schema = $self->schema;
157 my $databases = $self->databases;
158 my $dir = $self->upgrade_directory;
159 my $sqltargs = $self->sqltargs;
160 my $version = $schema->schema_version;
163 carp "Upgrade directory $dir does not exist, using ./\n";
168 my $sqlt = SQL::Translator->new({
170 ignore_constraint_names => 1,
171 ignore_index_names => 1,
172 parser => 'SQL::Translator::Parser::DBIx::Class',
176 my $sqlt_schema = $sqlt->translate({ data => $schema })
177 or $self->throw_exception ($sqlt->error);
179 foreach my $db (@$databases) {
181 $sqlt->{schema} = $sqlt_schema;
182 $sqlt->producer($db);
184 my $filename = $self->_ddl_schema_filename($db, $version, $dir);
186 carp "Overwriting existing DDL file - $filename";
190 my $output = $sqlt->translate;
192 carp("Failed to translate to $db, skipping. (" . $sqlt->error . ")");
196 unless( open $file, q(>), $filename ) {
197 $self->throw_exception("Can't open $filename for writing ($!)");
200 print {$file} $output;
206 my ($self, $from_version, $to_version, $version_set) = @_;
208 $from_version ||= $self->db_version;
209 $to_version ||= $self->schema_version;
211 # for updates prepared automatically (rob's stuff)
212 # one would want to explicitly set $version_set to
214 $version_set ||= [$from_version, $to_version];
215 my $schema = $self->schema;
216 my $databases = $self->databases;
217 my $dir = $self->upgrade_directory;
218 my $sqltargs = $self->sqltargs;
221 carp "Upgrade directory $dir does not exist, using ./\n";
225 my $schema_version = $schema->schema_version;
229 ignore_constraint_names => 1,
230 ignore_index_names => 1,
234 my $sqlt = SQL::Translator->new( $sqltargs );
236 $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
237 my $sqlt_schema = $sqlt->translate({ data => $schema })
238 or $self->throw_exception ($sqlt->error);
240 foreach my $db (@$databases) {
242 $sqlt->{schema} = $sqlt_schema;
243 $sqlt->producer($db);
245 my $prefilename = $self->_ddl_schema_filename($db, $from_version, $dir);
246 unless(-e $prefilename) {
247 carp("No previous schema file found ($prefilename)");
251 my $diff_file = $self->_ddl_schema_diff_filename($db, $version_set, $dir );
253 carp("Overwriting existing diff file - $diff_file");
259 my $t = SQL::Translator->new({
265 $t->parser( $db ) # could this really throw an exception?
266 or $self->throw_exception ($t->error);
268 my $out = $t->translate( $prefilename )
269 or $self->throw_exception ($t->error);
271 $source_schema = $t->schema;
273 $source_schema->name( $prefilename )
274 unless $source_schema->name;
277 # The "new" style of producers have sane normalization and can support
278 # diffing a SQL file against a DBIC->SQLT schema. Old style ones don't
279 # And we have to diff parsed SQL against parsed SQL.
280 my $dest_schema = $sqlt_schema;
282 unless ( "SQL::Translator::Producer::$db"->can('preprocess_schema') ) {
283 my $t = SQL::Translator->new({
289 $t->parser( $db ) # could this really throw an exception?
290 or $self->throw_exception ($t->error);
292 my $filename = $self->_ddl_schema_filename($db, $to_version, $dir);
293 my $out = $t->translate( $filename )
294 or $self->throw_exception ($t->error);
296 $dest_schema = $t->schema;
298 $dest_schema->name( $filename )
299 unless $dest_schema->name;
302 my $diff = SQL::Translator::Diff::schema_diff(
308 unless(open $file, q(>), $diff_file) {
309 $self->throw_exception("Can't write to $diff_file ($!)");
317 method _read_sql_file($file) {
320 open my $fh, '<', $file or carp("Can't open upgrade file, $file ($!)");
321 my @data = split /\n/, join '', <$fh>;
327 !/^(BEGIN|BEGIN TRANSACTION|COMMIT)/m
334 sub _upgrade_single_step {
336 my @version_set = @{ shift @_ };
337 my $upgrade_file = $self->_ddl_schema_diff_filename(
338 $self->storage->sqlt_type,
340 $self->upgrade_directory,
343 unless (-f $upgrade_file) {
345 carp "Upgrade not possible, no upgrade file found ($upgrade_file), please create one\n";
349 $self->_filedata($self->_read_sql_file($upgrade_file)); # I don't like this --fREW 2010-02-22
350 $self->schema->txn_do(sub { $self->_do_upgrade });
353 method _do_upgrade { $self->_run_upgrade(qr/.*?/) }
355 method _run_upgrade($stm) {
356 return unless $self->_filedata;
357 my @statements = grep { $_ =~ $stm } @{$self->_filedata};
360 $self->storage->debugobj->query_start($_) if $self->storage->debug;
361 $self->_apply_statement($_);
362 $self->storage->debugobj->query_end($_) if $self->storage->debug;
366 method _apply_statement($statement) {
368 $self->storage->dbh->do($_) or carp "SQL was: $_"
371 __PACKAGE__->meta->make_immutable;
377 vim: ts=2 sw=2 expandtab