start Role refactor
[dbsrgits/DBIx-Class-DeploymentHandler.git] / lib / DBIx / Class / DeploymentHandler / SqltDeployMethod.pm
1 package DBIx::Class::DeploymentHandler::SqltDeployMethod;
2 use Moose::Role;
3 use Method::Signatures::Simple;
4
5 use Carp 'carp';
6
7 method deployment_statements {
8   my $dir      = $self->upgrade_directory;
9   my $schema   = $self->schema;
10   my $type     = $self->storage->sqlt_type;
11   my $sqltargs = $self->sqltargs;
12   my $version  = $self->schema_version || '1.x';
13
14   my $filename = $self->ddl_filename($type, $version, $dir);
15   if(-f $filename) {
16       my $file;
17       open $file, q(<), $filename
18         or carp "Can't open $filename ($!)";
19       my @rows = <$file>;
20       close $file;
21       return join '', @rows;
22   }
23
24   # sources needs to be a parser arg, but for simplicty allow at top level
25   # coming in
26   $sqltargs->{parser_args}{sources} = delete $sqltargs->{sources}
27       if exists $sqltargs->{sources};
28
29   my $tr = SQL::Translator->new(
30     producer => "SQL::Translator::Producer::${type}",
31     %$sqltargs,
32     parser => 'SQL::Translator::Parser::DBIx::Class',
33     data => $schema,
34   );
35
36   my @ret;
37   my $wa = wantarray;
38   if ($wa) {
39     @ret = $tr->translate;
40   }
41   else {
42     $ret[0] = $tr->translate;
43   }
44
45   $schema->throw_exception( 'Unable to produce deployment statements: ' . $tr->error)
46     unless (@ret && defined $ret[0]);
47
48   return $wa ? @ret : $ret[0];
49 }
50
51 method deploy {
52   my $schema   = $self->schema;
53   my $type     = undef;
54   my $sqltargs = $self->sqltargs;
55   my $dir      = $self->upgrade_directory;
56   my $storage  = $self->storage;
57
58   my $deploy = sub {
59     my $line = shift;
60     return if(!$line || $line =~ /^--|^BEGIN TRANSACTION|^COMMIT|^\s+$/);
61     $storage->_query_start($line);
62     try {
63       # do a dbh_do cycle here, as we need some error checking in
64       # place (even though we will ignore errors)
65       $storage->dbh_do (sub { $_[1]->do($line) });
66     }
67     catch {
68       carp "$_ (running '${line}')"
69     }
70     $storage->_query_end($line);
71   };
72   my @statements = $self->deployment_statements();
73   if (@statements > 1) {
74     foreach my $statement (@statements) {
75       $deploy->( $statement );
76     }
77   }
78   elsif (@statements == 1) {
79     foreach my $line ( split(";\n", $statements[0])) {
80       $deploy->( $line );
81     }
82   }
83 }
84
85 method create_install_ddl {
86   my $schema    = $self->schema;
87   my $databases = $self->databases;
88   my $dir       = $self->upgrade_directory;
89   my $sqltargs  = $self->sqltargs;
90   unless( -d $dir ) {
91     carp "Upgrade directory $dir does not exist, using ./\n";
92     $dir = "./";
93   }
94
95   my $version = $schema->schema_version || '1.x';
96   my $schema_version = $schema->schema_version || '1.x';
97   $version ||= $schema_version;
98
99   $sqltargs = {
100     add_drop_table => 1,
101     ignore_constraint_names => 1,
102     ignore_index_names => 1,
103     %{$sqltargs || {}}
104   };
105
106   my $sqlt = SQL::Translator->new( $sqltargs );
107
108   $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
109   my $sqlt_schema = $sqlt->translate({ data => $schema })
110     or $self->throw_exception ($sqlt->error);
111
112   foreach my $db (@$databases) {
113     $sqlt->reset;
114     $sqlt->{schema} = $sqlt_schema;
115     $sqlt->producer($db);
116
117     my $filename = $self->ddl_filename($db, $version, $dir);
118     if (-e $filename && ($version eq $schema_version )) {
119       # if we are dumping the current version, overwrite the DDL
120       carp "Overwriting existing DDL file - $filename";
121       unlink $filename;
122     }
123
124     my $output = $sqlt->translate;
125     if(!$output) {
126       carp("Failed to translate to $db, skipping. (" . $sqlt->error . ")");
127       next;
128     }
129     my $file;
130     unless( open $file, q(>), $filename ) {
131       $self->throw_exception("Can't open $filename for writing ($!)");
132       next;
133     }
134     print {$file} $output;
135     close $file;
136   }
137 }
138
139 method create_update_ddl($version, $preversion) {
140   my $schema    = $self->schema;
141   my $databases = $self->databases;
142   my $dir       = $self->upgrade_directory;
143   my $sqltargs  = $self->sqltargs;
144
145   unless( -d $dir ) {
146     carp "Upgrade directory $dir does not exist, using ./\n";
147     $dir = "./";
148   }
149
150   my $schema_version = $schema->schema_version || '1.x';
151   $version ||= $schema_version;
152
153   $sqltargs = {
154     add_drop_table => 1,
155     ignore_constraint_names => 1,
156     ignore_index_names => 1,
157     %{$sqltargs}
158   };
159
160   my $sqlt = SQL::Translator->new( $sqltargs );
161
162   $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
163   my $sqlt_schema = $sqlt->translate({ data => $schema })
164     or $self->throw_exception ($sqlt->error);
165
166   foreach my $db (@$databases) {
167     $sqlt->reset;
168     $sqlt->{schema} = $sqlt_schema;
169     $sqlt->producer($db);
170
171     my $prefilename = $self->ddl_filename($db, $preversion, $dir);
172     unless(-e $prefilename) {
173       carp("No previous schema file found ($prefilename)");
174       next;
175     }
176
177     my $diff_file = $self->ddl_filename($db, $version, $dir, $preversion);
178     if(-e $diff_file) {
179       carp("Overwriting existing diff file - $diff_file");
180       unlink $diff_file;
181     }
182
183     my $source_schema;
184     {
185       my $t = SQL::Translator->new({
186          %{$sqltargs},
187          debug => 0,
188          trace => 0,
189       });
190
191       $t->parser( $db ) # could this really throw an exception?
192         or $self->throw_exception ($t->error);
193
194       my $out = $t->translate( $prefilename )
195         or $self->throw_exception ($t->error);
196
197       $source_schema = $t->schema;
198
199       $source_schema->name( $prefilename )
200         unless  $source_schema->name;
201     }
202
203     # The "new" style of producers have sane normalization and can support
204     # diffing a SQL file against a DBIC->SQLT schema. Old style ones don't
205     # And we have to diff parsed SQL against parsed SQL.
206     my $dest_schema = $sqlt_schema;
207
208     unless ( "SQL::Translator::Producer::$db"->can('preprocess_schema') ) {
209       my $t = SQL::Translator->new({
210          %{$sqltargs},
211          debug => 0,
212          trace => 0,
213       });
214
215       $t->parser( $db ) # could this really throw an exception?
216         or $self->throw_exception ($t->error);
217
218       my $filename = $self->ddl_filename($db, $version, $dir);
219       my $out = $t->translate( $filename )
220         or $self->throw_exception ($t->error);
221
222       $dest_schema = $t->schema;
223
224       $dest_schema->name( $filename )
225         unless $dest_schema->name;
226     }
227
228     my $diff = SQL::Translator::Diff::schema_diff(
229        $source_schema, $db,
230        $dest_schema,   $db,
231        $sqltargs
232     );
233     my $file;
234     unless(open $file, q(>), $diff_file) {
235       $self->throw_exception("Can't write to $diff_file ($!)");
236       next;
237     }
238     print {$file} $diff;
239     close $file;
240   }
241 }
242
243 method create_ddl_dir($version, $preversion) {
244   $self->create_install_ddl;
245   $self->create_update_ddl($version, $preversion) if $preversion;
246 }
247
248 1;