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