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