File Coverage

lib/DBIx/SchemaChecksum/App/ApplyChanges.pm
Criterion Covered Total %
statement 57 89 64.0
branch 21 40 52.5
condition n/a
subroutine 8 9 88.8
pod n/a
total 86 138 62.3


line stmt bran cond sub pod time code
1             package DBIx::SchemaChecksum::App::ApplyChanges;
2 3     3   364373 use 5.010;
  3         12  
3              
4             # ABSTRACT: Apply changes based on current checksum
5              
6 3     3   1541 use MooseX::App::Command;
  3         1999986  
  3         14  
7             extends qw(DBIx::SchemaChecksum::App);
8 3     3   2274719 use IO::Prompt::Tiny qw(prompt);
  3         1739  
  3         225  
9 3     3   29 use Try::Tiny;
  3         9  
  3         2723  
10              
11             option '+sqlsnippetdir' => ( required => 1);
12             option 'dry_run' => ( is => 'rw', isa => 'Bool', default => 0, documentation=>'Only list changes, do not apply' );
13             has 'no_prompt' => ( is => 'rw', isa => 'Bool', default => 0, documentation=>'Do not prompt, just use defaults');
14              
15             sub run {
16 1     1   2674 my $self = shift;
17              
18 1         7 $self->apply_sql_snippets($self->checksum);
19             }
20              
21              
22             sub apply_sql_snippets {
23 6     6   4650 my ($self, $this_checksum ) = @_;
24 6         245 my $update_path = $self->_update_path;
25              
26             my $update = $update_path->{$this_checksum}
27 6 50       37 if ( exists $update_path->{$this_checksum} );
28              
29 6 50       23 unless ($update) {
30 0         0 foreach my $update_entry (values %{$update_path}) {
  0         0  
31 0         0 my $post_checksum_index = 0;
32 0         0 while (@{$update_entry} > $post_checksum_index) {
  0         0  
33 0 0       0 if ($update_entry->[$post_checksum_index] eq 'SAME_CHECKSUM') {
34 0         0 $post_checksum_index++;
35 0         0 next;
36             }
37 0 0       0 if ($update_entry->[$post_checksum_index+1] eq $this_checksum) {
38 0         0 say "db checksum $this_checksum matching ".$update_entry->[$post_checksum_index]->relative;
39 0         0 return;
40             }
41 0         0 $post_checksum_index += 2;
42             }
43             }
44 0         0 say "No update found that's based on $this_checksum.";
45 0         0 return;
46             }
47              
48 6 100       53 if ( $update->[0] eq 'SAME_CHECKSUM' ) {
49 2 50       25 return unless $update->[1];
50 2         24 my ( $file, $expected_post_checksum ) = splice( @$update, 1, 2 );
51              
52 2         21 $self->apply_file( $file, $expected_post_checksum );
53             }
54             else {
55 4         303 $self->apply_file( @$update );
56             }
57             }
58              
59             sub apply_file {
60 6     6   25 my ( $self, $file, $expected_post_checksum ) = @_;
61 6         250 my $filename = $file->relative($self->sqlsnippetdir);
62              
63 6 100       1316 my $no_checksum_change = $self->checksum eq $expected_post_checksum ? 1 : 0;
64              
65 6         16 my $answer;
66 6 100       26 if ($no_checksum_change) {
67 1         6 $answer = prompt("Apply $filename (won't change checksum)? [y/n/s]",'y');
68             }
69             else {
70 5         27 $answer = prompt("Apply $filename? [y/n]",'y');
71             }
72              
73 6 50       760 if ($answer eq 'y') {
    0          
74 6 50       244 say "Starting to apply $filename" if $self->verbose;
75              
76 6         47 my $content = $file->slurp;
77              
78 6         2179 my $dbh = $self->dbh;
79 6         139 $dbh->begin_work;
80              
81 6         285 my $split_regex = qr/(?!:[\\]);/;
82              
83 6 50       37 if ($content =~ m/--\s*split-at:\s*(\S+)\n/s) {
84 0         0 say "Splitting $filename commands at >$1<";
85 0         0 $split_regex = qr/$1/;
86             }
87              
88 6         59 $content =~ s/^\s*--.+$//gm;
89 6         53 foreach my $command ( split( $split_regex , $content ) ) {
90 11         51 $command =~ s/\A\s+//;
91 11         40 $command =~ s/\s+\Z//;
92              
93 11 100       42 next unless $command;
94 5 50       156 say "Executing SQL statement: $command" if $self->verbose;
95             my $success = try {
96 5     5   740 $dbh->do($command);
97 5         2305 return 1;
98             }
99             catch {
100 0     0   0 $dbh->rollback;
101 0 0       0 say "SQL error: $_" unless $dbh->{PrintError};
102 0         0 say "ABORTING!";
103 0         0 return undef;
104 5         102 };
105 5 50       128 return unless $success; # abort all further changes
106 5 50       210 say "Successful!" if $self->verbose;
107             }
108              
109 6 50       280 if ( $self->dry_run ) {
110 0         0 $dbh->rollback;
111 0         0 say "dry run, so checksums cannot match. We proceed anyway...";
112 0         0 return $self->apply_sql_snippets($expected_post_checksum);
113             }
114              
115             # new checksum
116 6         227 $self->reset_checksum;
117 6         29 my $post_checksum = $self->checksum;
118              
119 6 50       31 if ( $post_checksum eq $expected_post_checksum ) {
120 6         337 say "post checksum OK";
121 6         79989 $dbh->commit;
122 6 100       770 if ($self->_update_path->{$post_checksum}) {
123 4         59 return $self->apply_sql_snippets($post_checksum);
124             }
125             else {
126 2         139 say 'No more changes';
127 2         108 return;
128             }
129             }
130             else {
131 0           say "post checksum mismatch!";
132 0           say " expected $expected_post_checksum";
133 0           say " got $post_checksum";
134 0           $dbh->rollback;
135 0           say "ABORTING!";
136 0           return;
137             }
138             }
139             elsif ($answer eq 's') {
140 0           return $self->apply_sql_snippets($expected_post_checksum);
141             }
142             else {
143 0           say "Not applying $filename, so we stop.";
144 0           return;
145             }
146             }
147              
148             __PACKAGE__->meta->make_immutable();
149             1;
150              
151             __END__
152              
153             =pod
154              
155             =encoding UTF-8
156              
157             =head1 NAME
158              
159             DBIx::SchemaChecksum::App::ApplyChanges - Apply changes based on current checksum
160              
161             =head1 VERSION
162              
163             version 1.102
164              
165             =head1 DESCRIPTION
166              
167             Apply all changes found in C<sqlsnippetdir> based on the current
168             checksum. For each file there will be a prompt asking if you want to
169             apply the file. Use C<--no_prompt> to always accept each change
170             (useful for deployment scripts etc). Use C<--dry_run> to run all
171             changes, but do not apply them.
172              
173             =head1 METHODS
174              
175             =head2 apply_sql_snippets
176              
177             $self->apply_sql_snippets( $starting_checksum );
178              
179             Applies SQL snippets in the correct order to the DB. Checks if the
180             checksum after applying the snippets is correct. If it isn't correct
181             rolls back the last change (if your DB supports transactions...)
182              
183             =head1 AUTHORS
184              
185             =over 4
186              
187             =item *
188              
189             Thomas Klausner <domm@cpan.org>
190              
191             =item *
192              
193             MaroÅ¡ Kollár <maros@cpan.org>
194              
195             =item *
196              
197             Klaus Ita <koki@worstofall.com>
198              
199             =back
200              
201             =head1 COPYRIGHT AND LICENSE
202              
203             This software is copyright (c) 2012 by Thomas Klausner, MaroÅ¡ Kollár, Klaus Ita.
204              
205             This is free software; you can redistribute it and/or modify it under
206             the same terms as the Perl 5 programming language system itself.
207              
208             =cut