fix upgrade_journal upgrade (ironic), RT#26078
[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 ( dbdef->table('areacode') and
75      dbdef->table('areacode')->primary_key eq 'code' )
76 {
77   if ( driver_name =~ /^mysql/i ) {
78     push @bugfix, 
79       'ALTER TABLE areacode DROP PRIMARY KEY',
80       'ALTER TABLE areacode ADD COLUMN (areanum int auto_increment primary key)';
81   }
82   else {
83     push @bugfix, 'ALTER TABLE areacode DROP CONSTRAINT areacode_pkey';
84   }
85 }
86
87 if ( dbdef->table('upgrade_journal') ) {
88   push @bugfix, "SELECT SETVAL( 'upgrade_journal_upgradenum_seq',
89                                 ( SELECT MAX(upgradenum) FROM upgrade_journal )
90                               )
91                 ";
92 }
93
94 if ( $DRY_RUN ) {
95   print
96     join(";\n", @bugfix ). ";\n";
97 } elsif ( @bugfix ) {
98
99   foreach my $statement ( @bugfix ) {
100     warn "$statement\n";
101     $dbh->do( $statement )
102       or die "Error: ". $dbh->errstr. "\n executing: $statement";
103   }
104
105   upgrade_schema();
106
107   dbdef_create($dbh, $dbdef_file);
108   delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
109   reload_dbdef($dbdef_file);
110
111 }
112
113 #you should have run fs-migrate-part_svc ages ago, when you upgraded
114 #from 1.3 to 1.4... if not, it needs to be hooked into -upgrade here or
115 #you'll lose all the part_svc settings it migrates to part_svc_column
116
117 my @statements = dbdef->sql_update_schema( dbdef_dist(datasrc),
118                                            $dbh,
119                                            { 'nullify_default' => 1, },
120                                          );
121
122 #### NEW CUSTOM FIELDS (prevent columns from being dropped by upgrade)
123 my $cfsth = $dbh->prepare("SELECT * FROM part_virtual_field") 
124                                                          or die $dbh->errstr;
125 $cfsth->execute or die $cfsth->errstr;
126 my $cf; 
127 # likely a very inefficient implementation of this
128 while ( $cf = $cfsth->fetchrow_hashref ) {
129     my $tbl = $cf->{'dbtable'};
130     my $name = $cf->{'name'};
131     @statements = grep { $_ !~ /^\s*ALTER\s+TABLE\s+$tbl\s+DROP\s+COLUMN\s+cf_$name\s*$/i }
132                                                                     @statements;
133 }
134
135 @statements = 
136   grep { $_ !~ /^CREATE +INDEX +h_queue/i } #useless, holds up queue insertion
137        @statements;
138
139 unless ( driver_name =~ /^mysql/i ) {
140   #not necessary under non-mysql, takes forever on big db
141   @statements =
142     grep { $_ !~ /^ *ALTER +TABLE +h_queue +ALTER +COLUMN +job +TYPE +varchar\(512\) *$/i }
143          @statements;
144 }
145
146 if ( $DRY_RUN ) {
147   print
148     join(";\n", @statements ). ";\n";
149   exit;
150 } else {
151   foreach my $statement ( @statements ) {
152     warn "$statement\n";
153     $dbh->do( $statement )
154       or die "Error: ". $dbh->errstr. "\n executing: $statement";
155   }
156
157 #  warn "Pre-schema change upgrades completed in ". (time-$start). " seconds\n"; # if $DEBUG;
158 #  $start = time;
159
160 #  dbdef->update_schema( dbdef_dist(datasrc), $dbh );
161 }
162
163 warn "Schema upgrade completed in ". (time-$start). " seconds\n"; # if $DEBUG;
164 $start = time;
165
166 my $hashref = {};
167 $hashref->{dry_run} = 1 if $DRY_RUN;
168 $hashref->{debug} = 1 if $DEBUG && $DRY_RUN;
169 prune_applications($hashref) unless $opt_s;
170
171 warn "Application pruning completed in ". (time-$start). " seconds\n"; # if $DEBUG;
172 $start = time;
173
174 print "\n" if $DRY_RUN;
175
176 if ( $dbh->{Driver}->{Name} =~ /^mysql/i && ! $opt_s ) {
177
178   foreach my $table (qw( svc_acct svc_phone )) {
179
180     my $sth = $dbh->prepare(
181       "SELECT COUNT(*) FROM duplicate_lock WHERE lockname = '$table'"
182     ) or die $dbh->errstr;
183
184     $sth->execute or die $sth->errstr;
185
186     unless ( $sth->fetchrow_arrayref->[0] ) {
187
188       $sth = $dbh->prepare(
189         "INSERT INTO duplicate_lock ( lockname ) VALUES ( '$table' )"
190       ) or die $dbh->errstr;
191
192       $sth->execute or die $sth->errstr;
193
194     }
195
196   }
197
198   warn "Duplication lock creation completed in ". (time-$start). " seconds\n"; # if $DEBUG;
199   $start = time;
200
201 }
202
203 $dbh->commit or die $dbh->errstr;
204
205 dbdef_create($dbh, $dbdef_file);
206
207 $dbh->disconnect or die $dbh->errstr;
208
209 delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
210 $FS::UID::AutoCommit = 0;
211 $FS::UID::callback_hack = 1;
212 $dbh = adminsuidsetup($user);
213 $FS::UID::callback_hack = 0;
214 unless ( $DRY_RUN || $opt_s ) {
215   my $dir = "%%%FREESIDE_CONF%%%/conf.". datasrc;
216   if (!scalar(qsearch('conf', {}))) {
217     my $error = FS::Conf::init_config($dir);
218     if ($error) {
219       warn "CONFIGURATION UPGRADE FAILED\n";
220       $dbh->rollback or die $dbh->errstr;
221       die $error;
222     }
223   }
224 }
225 $dbh->commit or die $dbh->errstr;
226 $dbh->disconnect or die $dbh->errstr;
227
228 $FS::UID::AutoCommit = 1;
229
230 $dbh = adminsuidsetup($user);
231
232 warn "Re-initialization with updated schema completed in ". (time-$start). " seconds\n"; # if $DEBUG;
233 $start = time;
234
235 upgrade_config()
236   unless $DRY_RUN || $opt_s;
237
238 $dbh->commit or die $dbh->errstr;
239
240 warn "Config updates completed in ". (time-$start). " seconds\n"; # if $DEBUG;
241 $start = time;
242
243 upgrade()
244   unless $DRY_RUN || $opt_s;
245
246 $dbh->commit or die $dbh->errstr;
247
248 warn "Table updates completed in ". (time-$start). " seconds\n"; # if $DEBUG;
249 $start = time;
250
251 upgrade_sqlradius()
252   unless $DRY_RUN || $opt_s || $opt_r;
253
254 warn "SQL RADIUS updates completed in ". (time-$start). " seconds\n"; # if $DEBUG;
255 $start = time;
256
257 $dbh->commit or die $dbh->errstr;
258 $dbh->disconnect or die $dbh->errstr;
259
260 warn "Final commit and disconnection completed in ". (time-$start). " seconds; upgrade done!\n"; # if $DEBUG;
261
262 ###
263
264 sub dbdef_create { # reverse engineer the schema from the DB and save to file
265   my( $dbh, $file ) = @_;
266   my $dbdef = new_native DBIx::DBSchema $dbh;
267   $dbdef->save($file);
268 }
269
270 sub usage {
271   die "Usage:\n  freeside-upgrade [ -d ] [ -r ] [ -s ] [ -q | -v ] user\n"; 
272 }
273
274 =head1 NAME
275
276 freeside-upgrade - Upgrades database schema for new freeside verisons.
277
278 =head1 SYNOPSIS
279
280   freeside-upgrade [ -d ] [ -r ] [ -s ] [ -q | -v ]
281
282 =head1 DESCRIPTION
283
284 Reads your existing database schema and updates it to match the current schema,
285 adding any columns or tables necessary.
286
287 Also performs other upgrade functions:
288
289 =over 4
290
291 =item Calls FS:: Misc::prune::prune_applications (probably unnecessary every upgrade, but simply won't find any records to change)
292
293 =item If necessary, moves your configuration information from the filesystem in /usr/local/etc/freeside/conf.<datasrc> to the database.
294
295 =back
296
297   [ -d ]: Dry run; output SQL statements (to STDOUT) only, but do not execute
298           them.
299
300   [ -q ]: Run quietly.  This may become the default at some point.
301
302   [ -r ]: Skip sqlradius updates.  Useful for occassions where the sqlradius
303           databases may be inaccessible.
304
305   [ -v ]: Run verbosely, sending debugging information to STDERR.  This is the
306           current default.
307
308   [ -s ]: Schema changes only.  Useful for Pg/slony slaves where the data
309           changes will be replicated from the Pg/slony master.
310
311 =head1 SEE ALSO
312
313 =cut
314