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