fix a series of unfortunate upgrades which resulted in too much payment receiptery...
[freeside.git] / FS / bin / freeside-upgrade
1 #!/usr/bin/perl -w
2
3 use strict;
4 use vars qw($opt_d $opt_s $opt_q $opt_v $opt_r);
5 use vars qw($DEBUG $DRY_RUN);
6 use Getopt::Std;
7 use DBIx::DBSchema 0.31; #0.39
8 use FS::UID qw(adminsuidsetup checkeuid datasrc driver_name);  #getsecrets);
9 use FS::CurrentUser;
10 use FS::Schema qw( dbdef dbdef_dist reload_dbdef );
11 use FS::Misc::prune qw(prune_applications);
12 use FS::Conf;
13 use FS::Record qw(qsearch);
14 use FS::Upgrade qw(upgrade_schema upgrade_config upgrade upgrade_sqlradius);
15
16 my $start = time;
17
18 die "Not running uid freeside!" unless checkeuid();
19
20 getopts("dqrs");
21
22 $DEBUG = !$opt_q;
23 #$DEBUG = $opt_v;
24
25 $DRY_RUN = $opt_d;
26
27 my $user = shift or die &usage;
28 $FS::CurrentUser::upgrade_hack = 1;
29 $FS::UID::callback_hack = 1;
30 my $dbh = adminsuidsetup($user);
31 $FS::UID::callback_hack = 0;
32
33 if ( driver_name =~ /^mysql/i ) { #until 0.39 is required above
34   eval "use DBIx::DBSchema 0.39;";
35   die $@ if $@;
36 }
37
38 #needs to match FS::Schema...
39 my $dbdef_file = "%%%FREESIDE_CONF%%%/dbdef.". datasrc;
40
41 dbdef_create($dbh, $dbdef_file);
42
43 delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
44 reload_dbdef($dbdef_file);
45
46 warn "Upgrade startup completed in ". (time-$start). " seconds\n"; # if $DEBUG;
47 $start = time;
48
49 #$DBIx::DBSchema::DEBUG = $DEBUG;
50 #$DBIx::DBSchema::Table::DEBUG = $DEBUG;
51 #$DBIx::DBSchema::Index::DEBUG = $DEBUG;
52
53 my @bugfix = ();
54
55 if (dbdef->table('cust_main')->column('agent_custid') && ! $opt_s) { 
56   push @bugfix,
57     "UPDATE cust_main SET agent_custid = NULL where agent_custid = ''";
58
59   push @bugfix,
60     "UPDATE h_cust_main SET agent_custid = NULL where agent_custid = ''"
61       if (dbdef->table('h_cust_main')); 
62 }
63
64 if ( dbdef->table('cgp_rule_condition') &&
65      dbdef->table('cgp_rule_condition')->column('condition') 
66    )
67 {
68   push @bugfix,
69    "ALTER TABLE ${_}cgp_rule_condition RENAME COLUMN condition TO conditionname"
70       for '', 'h_';
71
72 }
73
74 if ( $DRY_RUN ) {
75   print
76     join(";\n", @bugfix ). ";\n";
77 } elsif ( @bugfix ) {
78
79   foreach my $statement ( @bugfix ) {
80     warn "$statement\n";
81     $dbh->do( $statement )
82       or die "Error: ". $dbh->errstr. "\n executing: $statement";
83   }
84
85   upgrade_schema();
86
87   dbdef_create($dbh, $dbdef_file);
88   delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
89   reload_dbdef($dbdef_file);
90
91 }
92
93 #you should have run fs-migrate-part_svc ages ago, when you upgraded
94 #from 1.3 to 1.4... if not, it needs to be hooked into -upgrade here or
95 #you'll lose all the part_svc settings it migrates to part_svc_column
96
97 my @statements = dbdef->sql_update_schema( dbdef_dist(datasrc),
98                                            $dbh,
99                                            { 'nullify_default' => 1, },
100                                          );
101
102 @statements = 
103   grep { $_ !~ /^CREATE +INDEX +h_queue/i } #useless, holds up queue insertion
104        @statements;
105
106 unless ( driver_name =~ /^mysql/i ) {
107   #not necessary under non-mysql, takes forever on big db
108   @statements =
109     grep { $_ !~ /^ *ALTER +TABLE +h_queue +ALTER +COLUMN +job +TYPE +varchar\(512\) *$/i }
110          @statements;
111 }
112
113 if ( $DRY_RUN ) {
114   print
115     join(";\n", @statements ). ";\n";
116   exit;
117 } else {
118   foreach my $statement ( @statements ) {
119     warn "$statement\n";
120     $dbh->do( $statement )
121       or die "Error: ". $dbh->errstr. "\n executing: $statement";
122   }
123
124 #  warn "Pre-schema change upgrades completed in ". (time-$start). " seconds\n"; # if $DEBUG;
125 #  $start = time;
126
127 #  dbdef->update_schema( dbdef_dist(datasrc), $dbh );
128 }
129
130 warn "Schema upgrade completed in ". (time-$start). " seconds\n"; # if $DEBUG;
131 $start = time;
132
133 my $hashref = {};
134 $hashref->{dry_run} = 1 if $DRY_RUN;
135 $hashref->{debug} = 1 if $DEBUG && $DRY_RUN;
136 prune_applications($hashref) unless $opt_s;
137
138 warn "Application pruning completed in ". (time-$start). " seconds\n"; # if $DEBUG;
139 $start = time;
140
141 print "\n" if $DRY_RUN;
142
143 if ( $dbh->{Driver}->{Name} =~ /^mysql/i && ! $opt_s ) {
144
145   foreach my $table (qw( svc_acct svc_phone )) {
146
147     my $sth = $dbh->prepare(
148       "SELECT COUNT(*) FROM duplicate_lock WHERE lockname = '$table'"
149     ) or die $dbh->errstr;
150
151     $sth->execute or die $sth->errstr;
152
153     unless ( $sth->fetchrow_arrayref->[0] ) {
154
155       $sth = $dbh->prepare(
156         "INSERT INTO duplicate_lock ( lockname ) VALUES ( '$table' )"
157       ) or die $dbh->errstr;
158
159       $sth->execute or die $sth->errstr;
160
161     }
162
163   }
164
165   warn "Duplication lock creation completed in ". (time-$start). " seconds\n"; # if $DEBUG;
166   $start = time;
167
168 }
169
170 $dbh->commit or die $dbh->errstr;
171
172 dbdef_create($dbh, $dbdef_file);
173
174 $dbh->disconnect or die $dbh->errstr;
175
176 delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
177 $FS::UID::AutoCommit = 0;
178 $FS::UID::callback_hack = 1;
179 $dbh = adminsuidsetup($user);
180 $FS::UID::callback_hack = 0;
181 unless ( $DRY_RUN || $opt_s ) {
182   my $dir = "%%%FREESIDE_CONF%%%/conf.". datasrc;
183   if (!scalar(qsearch('conf', {}))) {
184     my $error = FS::Conf::init_config($dir);
185     if ($error) {
186       warn "CONFIGURATION UPGRADE FAILED\n";
187       $dbh->rollback or die $dbh->errstr;
188       die $error;
189     }
190   }
191 }
192 $dbh->commit or die $dbh->errstr;
193 $dbh->disconnect or die $dbh->errstr;
194
195 $FS::UID::AutoCommit = 1;
196
197 $dbh = adminsuidsetup($user);
198
199 warn "Re-initialization with updated schema completed in ". (time-$start). " seconds\n"; # if $DEBUG;
200 $start = time;
201
202 upgrade_config()
203   unless $DRY_RUN || $opt_s;
204
205 $dbh->commit or die $dbh->errstr;
206
207 warn "Config updates completed in ". (time-$start). " seconds\n"; # if $DEBUG;
208 $start = time;
209
210 upgrade()
211   unless $DRY_RUN || $opt_s;
212
213 $dbh->commit or die $dbh->errstr;
214
215 warn "Table updates completed in ". (time-$start). " seconds\n"; # if $DEBUG;
216 $start = time;
217
218 upgrade_sqlradius()
219   unless $DRY_RUN || $opt_s || $opt_r;
220
221 warn "SQL RADIUS updates completed in ". (time-$start). " seconds\n"; # if $DEBUG;
222 $start = time;
223
224 $dbh->commit or die $dbh->errstr;
225 $dbh->disconnect or die $dbh->errstr;
226
227 warn "Final commit and disconnection completed in ". (time-$start). " seconds; upgrade done!\n"; # if $DEBUG;
228
229 ###
230
231 sub dbdef_create { # reverse engineer the schema from the DB and save to file
232   my( $dbh, $file ) = @_;
233   my $dbdef = new_native DBIx::DBSchema $dbh;
234   $dbdef->save($file);
235 }
236
237 sub usage {
238   die "Usage:\n  freeside-upgrade [ -d ] [ -r ] [ -s ] [ -q | -v ] user\n"; 
239 }
240
241 =head1 NAME
242
243 freeside-upgrade - Upgrades database schema for new freeside verisons.
244
245 =head1 SYNOPSIS
246
247   freeside-upgrade [ -d ] [ -r ] [ -s ] [ -q | -v ]
248
249 =head1 DESCRIPTION
250
251 Reads your existing database schema and updates it to match the current schema,
252 adding any columns or tables necessary.
253
254 Also performs other upgrade functions:
255
256 =over 4
257
258 =item Calls FS:: Misc::prune::prune_applications (probably unnecessary every upgrade, but simply won't find any records to change)
259
260 =item If necessary, moves your configuration information from the filesystem in /usr/local/etc/freeside/conf.<datasrc> to the database.
261
262 =back
263
264   [ -d ]: Dry run; output SQL statements (to STDOUT) only, but do not execute
265           them.
266
267   [ -q ]: Run quietly.  This may become the default at some point.
268
269   [ -r ]: Skip sqlradius updates.  Useful for occassions where the sqlradius
270           databases may be inaccessible.
271
272   [ -v ]: Run verbosely, sending debugging information to STDERR.  This is the
273           current default.
274
275   [ -s ]: Schema changes only.  Useful for Pg/slony slaves where the data
276           changes will be replicated from the Pg/slony master.
277
278 =head1 SEE ALSO
279
280 =cut
281