summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Install/DB.pm549
-rw-r--r--Bugzilla/Install/Filesystem.pm5
-rwxr-xr-xchecksetup.pl491
3 files changed, 570 insertions, 475 deletions
diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm
index c30dbf2dd..1e8dcb693 100644
--- a/Bugzilla/Install/DB.pm
+++ b/Bugzilla/Install/DB.pm
@@ -32,7 +32,7 @@ use Date::Format;
use IO::File;
use base qw(Exporter);
-our @EXPORT = qw(
+our @EXPORT_OK = qw(
indicate_progress
);
@@ -43,11 +43,69 @@ sub indicate_progress {
my $every = $params->{every} || 1;
print "." if !($current % $every);
- if ($current % ($every * 50) == 0) {
+ if ($current % ($every * 60) == 0) {
print "$current/$total (" . int($current * 100 / $total) . "%)\n";
}
}
+# NOTE: This is NOT the function for general table updates. See
+# update_table_definitions for that. This is only for the fielddefs table.
+sub update_fielddefs_definition {
+ my $dbh = Bugzilla->dbh;
+
+ # 2005-02-21 - LpSolit@gmail.com - Bug 279910
+ # qacontact_accessible and assignee_accessible field names no longer exist
+ # in the 'bugs' table. Their corresponding entries in the 'bugs_activity'
+ # table should therefore be marked as obsolete, meaning that they cannot
+ # be used anymore when querying the database - they are not deleted in
+ # order to keep track of these fields in the activity table.
+ if (!$dbh->bz_column_info('fielddefs', 'obsolete')) {
+ $dbh->bz_add_column('fielddefs', 'obsolete',
+ {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
+ print "Marking qacontact_accessible and assignee_accessible as",
+ " obsolete fields...\n";
+ $dbh->do("UPDATE fielddefs SET obsolete = 1
+ WHERE name = 'qacontact_accessible'
+ OR name = 'assignee_accessible'");
+ }
+
+ # 2005-08-10 Myk Melez <myk@mozilla.org> bug 287325
+ # Record each field's type and whether or not it's a custom field,
+ # in fielddefs.
+ $dbh->bz_add_column('fielddefs', 'type',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+ $dbh->bz_add_column('fielddefs', 'custom',
+ {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
+
+ $dbh->bz_add_column('fielddefs', 'enter_bug',
+ {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
+
+ # Change the name of the fieldid column to id, so that fielddefs
+ # can use Bugzilla::Object easily. We have to do this up here, because
+ # otherwise adding these field definitions will fail.
+ $dbh->bz_rename_column('fielddefs', 'fieldid', 'id');
+
+ # If the largest fielddefs sortkey is less than 100, then
+ # we're using the old sorting system, and we should convert
+ # it to the new one before adding any new definitions.
+ if (!$dbh->selectrow_arrayref(
+ 'SELECT COUNT(id) FROM fielddefs WHERE sortkey >= 100'))
+ {
+ print "Updating the sortkeys for the fielddefs table...\n";
+ my $field_ids = $dbh->selectcol_arrayref(
+ 'SELECT id FROM fielddefs ORDER BY sortkey');
+ my $sortkey = 100;
+ foreach my $field_id (@$field_ids) {
+ $dbh->do('UPDATE fielddefs SET sortkey = ? WHERE id = ?',
+ undef, $sortkey, $field_id);
+ $sortkey += 100;
+ }
+ }
+
+ # Remember, this is not the function for adding general table changes.
+ # That is below. Add new changes to the fielddefs table above this
+ # comment.
+}
# Small changes can be put directly into this function.
# However, larger changes (more than three or four lines) should
@@ -326,7 +384,79 @@ sub update_table_definitions {
_initialize_dependency_tree_changes_email_pref();
_change_all_mysql_booleans_to_tinyint();
+ # make classification_id field type be consistent with DB:Schema
+ $dbh->bz_alter_column('products', 'classification_id',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => '1'});
+
+ # initialowner was accidentally NULL when we checked-in Schema,
+ # when it really should be NOT NULL.
+ $dbh->bz_alter_column('components', 'initialowner',
+ {TYPE => 'INT3', NOTNULL => 1}, 0);
+
+ # 2005-03-28 - bug 238800 - index flags.type_id for editflagtypes.cgi
+ $dbh->bz_add_index('flags', 'flags_type_id_idx', [qw(type_id)]);
+
+ # For a short time, the flags_type_id_idx was misnamed in upgraded installs.
+ $dbh->bz_drop_index('flags', 'type_id');
+
+ # 2005-04-28 - LpSolit@gmail.com - Bug 7233: add an index to versions
+ $dbh->bz_alter_column('versions', 'value',
+ {TYPE => 'varchar(64)', NOTNULL => 1});
+ $dbh->bz_add_index('versions', 'versions_product_id_idx',
+ {TYPE => 'UNIQUE', FIELDS => [qw(product_id value)]});
+ if (!exists $dbh->bz_column_info('milestones', 'sortkey')->{DEFAULT}) {
+ $dbh->bz_alter_column('milestones', 'sortkey',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+ }
+
+ # 2005-06-14 - LpSolit@gmail.com - Bug 292544
+ $dbh->bz_alter_column('bugs', 'creation_ts', {TYPE => 'DATETIME'});
+
+ _fix_whine_queries_title_and_op_sys_value();
+ _fix_attachments_submitter_id_idx();
+ _copy_attachments_thedata_to_attach_data();
+ _fix_broken_all_closed_series();
+
+ # PUBLIC is a reserved word in Oracle.
+ $dbh->bz_rename_column('series', 'public', 'is_public');
+
+ # 2005-09-28 bugreport@peshkin.net Bug 149504
+ $dbh->bz_add_column('attachments', 'isurl',
+ {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
+
+ # 2005-10-21 LpSolit@gmail.com - Bug 313020
+ $dbh->bz_add_column('namedqueries', 'query_type',
+ {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
+
+ # 2005-11-04 LpSolit@gmail.com - Bug 305927
+ $dbh->bz_alter_column('groups', 'userregexp',
+ {TYPE => 'TINYTEXT', NOTNULL => 1, DEFAULT => "''"});
+
+ # 2005-09-26 - olav@bkor.dhs.org - Bug 119524
+ $dbh->bz_alter_column('logincookies', 'cookie',
+ {TYPE => 'varchar(16)', PRIMARYKEY => 1, NOTNULL => 1});
+
+ _clean_control_characters_from_short_desc();
+
+ # 2005-12-07 altlst@sonic.net -- Bug 225221
+ $dbh->bz_add_column('longdescs', 'comment_id',
+ {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
+
+ _stop_storing_inactive_flags();
+ _change_short_desc_from_mediumtext_to_varchar();
+
+ # 2006-07-01 wurblzap@gmail.com -- Bug 69000
+ $dbh->bz_add_column('namedqueries', 'id',
+ {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
+ _move_namedqueries_linkinfooter_to_its_own_table();
+
+ _add_classifications_sortkey();
+ _move_data_nomail_into_db();
+
+ ################################################################
+ # New --TABLE-- changes should go *** A B O V E *** this point #
+ ################################################################
}
# Subroutines should be ordered in the order that they are called.
@@ -2148,6 +2278,378 @@ sub _change_all_mysql_booleans_to_tinyint {
}
}
+sub _fix_whine_queries_title_and_op_sys_value {
+ my $dbh = Bugzilla->dbh;
+ if (!exists $dbh->bz_column_info('whine_queries', 'title')->{DEFAULT}) {
+ # The below change actually has nothing to do with the whine_queries
+ # change, it just has to be contained within a schema change so that
+ # it doesn't run every time we run checksetup.
+
+ # Old Bugzillas have "other" as an OS choice, new ones have "Other"
+ # (capital O).
+ print "Setting any 'other' op_sys to 'Other'...\n";
+ $dbh->do('UPDATE op_sys SET value = ? WHERE value = ?',
+ undef, "Other", "other");
+ $dbh->do('UPDATE bugs SET op_sys = ? WHERE op_sys = ?',
+ undef, "Other", "other");
+ if (Bugzilla->params->{'defaultopsys'} eq 'other') {
+ # We can't actually fix the param here, because WriteParams() will
+ # make $datadir/params unwriteable to the webservergroup.
+ # It's too much of an ugly hack to copy the permission-fixing code
+ # down to here. (It would create more potential future bugs than
+ # it would solve problems.)
+ print "WARNING: Your 'defaultopsys' param is set to 'other', but"
+ . " Bugzilla now\n"
+ . " uses 'Other' (capital O).\n";
+ }
+
+ # Add a DEFAULT to whine_queries stuff so that editwhines.cgi
+ # works on PostgreSQL.
+ $dbh->bz_alter_column('whine_queries', 'title', {TYPE => 'varchar(128)',
+ NOTNULL => 1, DEFAULT => "''"});
+ }
+}
+
+sub _fix_attachments_submitter_id_idx {
+ my $dbh = Bugzilla->dbh;
+ # 2005-06-29 bugreport@peshkin.net, bug 299156
+ if ($dbh->bz_index_info('attachments', 'attachments_submitter_id_idx')
+ && (scalar(@{$dbh->bz_index_info('attachments',
+ 'attachments_submitter_id_idx'
+ )->{FIELDS}}) < 2))
+ {
+ $dbh->bz_drop_index('attachments', 'attachments_submitter_id_idx');
+ }
+ $dbh->bz_add_index('attachments', 'attachments_submitter_id_idx',
+ [qw(submitter_id bug_id)]);
+}
+
+sub _copy_attachments_thedata_to_attach_data {
+ my $dbh = Bugzilla->dbh;
+ # 2005-08-25 - bugreport@peshkin.net - Bug 305333
+ if ($dbh->bz_column_info("attachments", "thedata")) {
+ print "Migrating attachment data to its own table...\n";
+ print "(This may take a very long time)\n";
+ $dbh->do("INSERT INTO attach_data (id, thedata)
+ SELECT attach_id, thedata FROM attachments");
+ $dbh->bz_drop_column("attachments", "thedata");
+ }
+}
+
+sub _fix_broken_all_closed_series {
+ my $dbh = Bugzilla->dbh;
+
+ # 2005-11-26 - wurblzap@gmail.com - Bug 300473
+ # Repair broken automatically generated series queries for non-open bugs.
+ my $broken_series_indicator =
+ 'field0-0-0=resolution&type0-0-0=notequals&value0-0-0=---';
+ my $broken_nonopen_series =
+ $dbh->selectall_arrayref("SELECT series_id, query FROM series
+ WHERE query LIKE '$broken_series_indicator%'");
+ if (@$broken_nonopen_series) {
+ print 'Repairing broken series...';
+ my $sth_nuke =
+ $dbh->prepare('DELETE FROM series_data WHERE series_id = ?');
+ # This statement is used to repair a series by replacing the broken
+ # query with the correct one.
+ my $sth_repair =
+ $dbh->prepare('UPDATE series SET query = ? WHERE series_id = ?');
+ # The corresponding series for open bugs look like one of these two
+ # variations (bug 225687 changed the order of bug states).
+ # This depends on the set of bug states representing open bugs not
+ # to have changed since series creation.
+ my $open_bugs_query_base_old =
+ join("&", map { "bug_status=" . url_quote($_) }
+ ('UNCONFIRMED', 'NEW', 'ASSIGNED', 'REOPENED'));
+ my $open_bugs_query_base_new =
+ join("&", map { "bug_status=" . url_quote($_) } BUG_STATE_OPEN);
+ my $sth_openbugs_series =
+ $dbh->prepare("SELECT series_id FROM series WHERE query IN (?, ?)");
+ # Statement to find the series which has collected the most data.
+ my $sth_data_collected =
+ $dbh->prepare('SELECT count(*) FROM series_data
+ WHERE series_id = ?');
+ # Statement to select a broken non-open bugs count data entry.
+ my $sth_select_broken_nonopen_data =
+ $dbh->prepare('SELECT series_date, series_value FROM series_data' .
+ ' WHERE series_id = ?');
+ # Statement to select an open bugs count data entry.
+ my $sth_select_open_data =
+ $dbh->prepare('SELECT series_value FROM series_data' .
+ ' WHERE series_id = ? AND series_date = ?');
+ # Statement to fix a broken non-open bugs count data entry.
+ my $sth_fix_broken_nonopen_data =
+ $dbh->prepare('UPDATE series_data SET series_value = ?' .
+ ' WHERE series_id = ? AND series_date = ?');
+ # Statement to delete an unfixable broken non-open bugs count data
+ # entry.
+ my $sth_delete_broken_nonopen_data =
+ $dbh->prepare('DELETE FROM series_data' .
+ ' WHERE series_id = ? AND series_date = ?');
+ foreach (@$broken_nonopen_series) {
+ my ($broken_series_id, $nonopen_bugs_query) = @$_;
+
+ # Determine the product-and-component part of the query.
+ if ($nonopen_bugs_query =~ /^$broken_series_indicator(.*)$/) {
+ my $prodcomp = $1;
+
+ # If there is more than one series for the corresponding
+ # open-bugs series, we pick the one with the most data,
+ # which should be the one which was generated on creation.
+ # It's a pity we can't do subselects.
+ $sth_openbugs_series->execute(
+ $open_bugs_query_base_old . $prodcomp,
+ $open_bugs_query_base_new . $prodcomp);
+
+ my ($found_open_series_id, $datacount) = (undef, -1);
+ foreach my $open_ser_id ($sth_openbugs_series->fetchrow_array) {
+ $sth_data_collected->execute($open_ser_id);
+ my ($this_datacount) = $sth_data_collected->fetchrow_array;
+ if ($this_datacount > $datacount) {
+ $datacount = $this_datacount;
+ $found_open_series_id = $open_ser_id;
+ }
+ }
+
+ if ($found_open_series_id) {
+ # Move along corrupted series data and correct it. The
+ # corruption consists of it being the number of all bugs
+ # instead of the number of non-open bugs, so we calculate
+ # the correct count by subtracting the number of open bugs.
+ # If there is no corresponding open-bugs count for some
+ # reason (shouldn't happen), we drop the data entry.
+ print " $broken_series_id...";
+ $sth_select_broken_nonopen_data->execute($broken_series_id);
+ while (my $rowref =
+ $sth_select_broken_nonopen_data->fetchrow_arrayref)
+ {
+ my ($date, $broken_value) = @$rowref;
+ my ($openbugs_value) =
+ $dbh->selectrow_array($sth_select_open_data, undef,
+ $found_open_series_id, $date);
+ if (defined($openbugs_value)) {
+ $sth_fix_broken_nonopen_data->execute
+ ($broken_value - $openbugs_value,
+ $broken_series_id, $date);
+ }
+ else {
+ print <<EOT;
+
+WARNING - During repairs of series $broken_series_id, the irreparable data
+entry for date $date was encountered and is being deleted.
+
+Continuing repairs...
+EOT
+ $sth_delete_broken_nonopen_data->execute
+ ($broken_series_id, $date);
+ }
+ }
+
+ # Fix the broken query so that it collects correct data
+ # in the future.
+ $nonopen_bugs_query =~
+ s/^$broken_series_indicator/field0-0-0=resolution&type0-0-0=regexp&value0-0-0=./;
+ $sth_repair->execute($nonopen_bugs_query,
+ $broken_series_id);
+ }
+ else {
+ print <<EOT;
+
+WARNING - Series $broken_series_id was meant to collect non-open bug
+counts, but it has counted all bugs instead. It cannot be repaired
+automatically because no series that collected open bug counts was found.
+You'll probably want to delete or repair collected data for
+series $broken_series_id manually
+
+Continuing repairs...
+EOT
+ } # if ($found_open_series_id)
+ } # if ($nonopen_bugs_query =~
+ } # foreach (@$broken_nonopen_series)
+ print " done.\n";
+ } # if (@$broken_nonopen_series)
+}
+
+sub _clean_control_characters_from_short_desc {
+ my $dbh = Bugzilla->dbh;
+
+ # Fixup for Bug 101380
+ # "Newlines, nulls, leading/trailing spaces are getting into summaries"
+
+ my $controlchar_bugs =
+ $dbh->selectall_arrayref("SELECT short_desc, bug_id FROM bugs WHERE " .
+ $dbh->sql_regexp('short_desc', "'[[:cntrl:]]'"));
+ if (scalar(@$controlchar_bugs)) {
+ my $msg = 'Cleaning control characters from bug summaries...';
+ my $found = 0;
+ foreach (@$controlchar_bugs) {
+ my ($short_desc, $bug_id) = @$_;
+ my $clean_short_desc = clean_text($short_desc);
+ if ($clean_short_desc ne $short_desc) {
+ print $msg if !$found;
+ $found = 1;
+ print " $bug_id...";
+ $dbh->do("UPDATE bugs SET short_desc = ? WHERE bug_id = ?",
+ undef, $clean_short_desc, $bug_id);
+ }
+ }
+ print " done.\n" if $found;
+ }
+}
+
+sub _stop_storing_inactive_flags {
+ my $dbh = Bugzilla->dbh;
+ # 2006-03-02 LpSolit@gmail.com - Bug 322285
+ # Do not store inactive flags in the DB anymore.
+ if ($dbh->bz_column_info('flags', 'id')->{'TYPE'} eq 'INT3') {
+ # We first have to remove all existing inactive flags.
+ if ($dbh->bz_column_info('flags', 'is_active')) {
+ $dbh->do('DELETE FROM flags WHERE is_active = 0');
+ }
+
+ # Now we convert the id column to the auto_increment format.
+ $dbh->bz_alter_column('flags', 'id',
+ {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
+
+ # And finally, we remove the is_active column.
+ $dbh->bz_drop_column('flags', 'is_active');
+ }
+}
+
+sub _change_short_desc_from_mediumtext_to_varchar {
+ my $dbh = Bugzilla->dbh;
+ # short_desc should not be a mediumtext, fix anything longer than 255 chars.
+ if($dbh->bz_column_info('bugs', 'short_desc')->{TYPE} eq 'MEDIUMTEXT') {
+ # Move extremely long summaries into a comment ("from" the Reporter),
+ # and then truncate the summary.
+ my $long_summary_bugs = $dbh->selectall_arrayref(
+ 'SELECT bug_id, short_desc, reporter
+ FROM bugs WHERE LENGTH(short_desc) > 255');
+
+ if (@$long_summary_bugs) {
+ print <<EOT;
+
+WARNING: Some of your bugs had summaries longer than 255 characters.
+They have had their original summary copied into a comment, and then
+the summary was truncated to 255 characters. The affected bug numbers were:
+EOT
+ my $comment_sth = $dbh->prepare(
+ 'INSERT INTO longdescs (bug_id, who, thetext, bug_when)
+ VALUES (?, ?, ?, NOW())');
+ my $desc_sth = $dbh->prepare('UPDATE bugs SET short_desc = ?
+ WHERE bug_id = ?');
+ my @affected_bugs;
+ foreach my $bug (@$long_summary_bugs) {
+ my ($bug_id, $summary, $reporter_id) = @$bug;
+ my $summary_comment = "The original summary for this bug"
+ . " was longer than 255 characters, and so it was truncated"
+ . " when Bugzilla was upgraded. The original summary was:"
+ . "\n\n$summary";
+ $comment_sth->execute($bug_id, $reporter_id, $summary_comment);
+ my $short_summary = substr($summary, 0, 252) . "...";
+ $desc_sth->execute($short_summary, $bug_id);
+ push(@affected_bugs, $bug_id);
+ }
+ print join(', ', @affected_bugs) . "\n\n";
+ }
+
+ $dbh->bz_alter_column('bugs', 'short_desc', {TYPE => 'varchar(255)',
+ NOTNULL => 1});
+ }
+}
+
+sub _move_namedqueries_linkinfooter_to_its_own_table {
+ my $dbh = Bugzilla->dbh;
+ if ($dbh->bz_column_info("namedqueries", "linkinfooter")) {
+ # Move link-in-footer information into a table of its own.
+ my $sth_read = $dbh->prepare('SELECT id, userid
+ FROM namedqueries
+ WHERE linkinfooter = 1');
+ my $sth_write = $dbh->prepare('INSERT INTO namedqueries_link_in_footer
+ (namedquery_id, user_id) VALUES (?, ?)');
+ $sth_read->execute();
+ while (my ($id, $userid) = $sth_read->fetchrow_array()) {
+ $sth_write->execute($id, $userid);
+ }
+ $dbh->bz_drop_column("namedqueries", "linkinfooter");
+ }
+}
+
+sub _add_classifications_sortkey {
+ my $dbh = Bugzilla->dbh;
+ # 2006-07-07 olav@bkor.dhs.org - Bug 277377
+ # Add a sortkey to the classifications
+ if (!$dbh->bz_column_info('classifications', 'sortkey')) {
+ $dbh->bz_add_column('classifications', 'sortkey',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+
+ my $class_ids = $dbh->selectcol_arrayref(
+ 'SELECT id FROM classifications ORDER BY name');
+ my $sth = $dbh->prepare('UPDATE classifications SET sortkey = ? ' .
+ 'WHERE id = ?');
+ my $sortkey = 0;
+ foreach my $class_id (@$class_ids) {
+ $sth->execute($sortkey, $class_id);
+ $sortkey += 100;
+ }
+ }
+}
+
+sub _move_data_nomail_into_db {
+ my $dbh = Bugzilla->dbh;
+ my $datadir = bz_locations()->{'datadir'};
+ # 2006-07-14 karl@kornel.name - Bug 100953
+ # If a nomail file exists, move its contents into the DB
+ $dbh->bz_add_column('profiles', 'disable_mail',
+ { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE' });
+ if (-e "$datadir/nomail") {
+ # We have a data/nomail file, read it in and delete it
+ my %nomail;
+ print "Found a data/nomail file. Moving nomail entries into DB...\n";
+ my $nomail_file = new IO::File("$datadir/nomail", 'r');
+ while (<$nomail_file>) {
+ $nomail{trim($_)} = 1;
+ }
+ $nomail_file->close;
+
+ # Go through each entry read. If a user exists, set disable_mail.
+ my $query = $dbh->prepare('UPDATE profiles
+ SET disable_mail = 1
+ WHERE userid = ?');
+ foreach my $user_to_check (keys %nomail) {
+ my $uid;
+ if ($uid = Bugzilla::User::login_to_id($user_to_check)) {
+ my $user = new Bugzilla::User($uid);
+ print "\tDisabling email for user ", $user->email, "\n";
+ $query->execute($user->id);
+ delete $nomail{$user->email};
+ }
+ }
+
+ # If there are any nomail entries remaining, move them to nomail.bad
+ # and say something to the user.
+ if (scalar(keys %nomail)) {
+ print <<EOT;
+
+WARNING: The following users were listed in data/nomail, but do not
+have an account here. The unmatched entries have been moved
+to $datadir/nomail.bad:
+EOT
+ my $nomail_bad = new IO::File("$datadir/nomail.bad", '>>');
+ foreach my $unknown_user (keys %nomail) {
+ print "\t$unknown_user\n";
+ print $nomail_bad "$unknown_user\n";
+ delete $nomail{$unknown_user};
+ }
+ $nomail_bad->close;
+ print "\n";
+ }
+
+ # Now that we don't need it, get rid of the nomail file.
+ unlink "$datadir/nomail";
+ }
+}
+
1;
__END__
@@ -2158,17 +2660,54 @@ Bugzilla::Install::DB - Fix up the database during installation.
=head1 SYNOPSIS
+ use Bugzilla::Install::DB qw(indicate_progress);
+ Bugzilla::Install::DB::update_table_definitions();
+
+ indicate_progress({ total => $total, current => $count, every => 10 });
+
=head1 DESCRIPTION
This module is used primarily by L<checksetup.pl> to modify the
database during upgrades.
+=head1 SUBROUTINES
+
=over
-=back
+=item C<update_table_definitions()>
-=head1 SUBROUTINES
+Description: This is the primary code that updates table definitions
+ during upgrades. If you modify the schema in some
+ way, you should add code to the end of this function to
+ make sure that your modifications happen over all installations.
-=over
+Params: none
+
+Returns: nothing
+
+=item C<update_fielddefs_definition()>
+
+Description: L<checksetup.pl> depends on the fielddefs table having
+ its schema adjusted before the rest of the tables. So
+ these schema updates happen in a separate function from
+ L</update_table_definitions()>.
+
+Params: none
+
+Returns: nothing
+
+=item C<indicate_progress({ total => $total, current => $count, every => 1 })>
+
+Description: This prints out lines of dots as a long update is going on,
+ to let the user know where we are and that we're not frozen.
+ A new line of dots will start every 60 dots.
+
+Params: C<total> - The total number of items we're processing.
+ C<current> - The number of the current item we're processing.
+ C<every> - How often the function should print out a dot.
+ For example, if this is 10, the function will print out
+ a dot every ten items.
+
+Returns: nothing
=back
diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm
index d408a70e7..f9a5a4083 100644
--- a/Bugzilla/Install/Filesystem.pm
+++ b/Bugzilla/Install/Filesystem.pm
@@ -348,6 +348,11 @@ EOT
rmtree("shadow");
}
+ if (-e "$datadir/versioncache") {
+ print "Removing versioncache...\n";
+ unlink "$datadir/versioncache";
+ }
+
}
sub create_htaccess {
diff --git a/checksetup.pl b/checksetup.pl
index ae3cda5e5..15db11053 100755
--- a/checksetup.pl
+++ b/checksetup.pl
@@ -172,6 +172,19 @@ Note: sometimes those special comments occur more than once. For
example, C<--LOCAL--> is used at least 3 times in this code! C<--TABLE-->
is also used more than once, so search for each and every occurrence!
+=head2 Modifying the Database
+
+Sometimes you'll want to modify the database. In fact, that's mostly
+what checksetup does, is upgrade old Bugzilla databases to the modern
+format.
+
+If you'd like to know how to make changes to the datbase, see
+the information in the Bugzilla Developer's Guide, at:
+L<http:E<sol>E<sol>www.bugzilla.orgE<sol>docsE<sol>developer.html#sql-schema>
+
+Also see L<Bugzilla::DB/"Schema Modification Methods"> and
+L<Bugzilla::DB/"Schema Information Methods">.
+
=head1 RUNNING CHECKSETUP NON-INTERACTIVELY
To operate checksetup non-interactively, run it with a single argument
@@ -203,14 +216,12 @@ function calls needs to be specified in this file.
=head1 SEE ALSO
-L<Bugzilla::Install::Requirements>
-
-L<Bugzilla::Install::Localconfig>
-
-L<Bugzilla::Install::Filesystem>
-
-L<Bugzilla::Config/update_params>
-
+L<Bugzilla::Install::Requirements>,
+L<Bugzilla::Install::Localconfig>,
+L<Bugzilla::Install::Filesystem>,
+L<Bugzilla::Install::DB>,
+L<Bugzilla::Install>,
+L<Bugzilla::Config/update_params>, and
L<Bugzilla::DB/CONNECTION>
=cut
@@ -516,54 +527,7 @@ use constant OLD_FIELD_DEFS => (
# Calling Bugzilla::Field::create_or_update depends on the
# fielddefs table having a modern definition. So, we have to make
# these particular schema changes before we make any other schema changes.
-
-# 2005-02-21 - LpSolit@gmail.com - Bug 279910
-# qacontact_accessible and assignee_accessible field names no longer exist
-# in the 'bugs' table. Their corresponding entries in the 'bugs_activity'
-# table should therefore be marked as obsolete, meaning that they cannot
-# be used anymore when querying the database - they are not deleted in
-# order to keep track of these fields in the activity table.
-if (!$dbh->bz_column_info('fielddefs', 'obsolete')) {
- $dbh->bz_add_column('fielddefs', 'obsolete',
- {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
- print "Marking qacontact_accessible and assignee_accessible as obsolete",
- " fields...\n";
- $dbh->do("UPDATE fielddefs SET obsolete = 1
- WHERE name = 'qacontact_accessible'
- OR name = 'assignee_accessible'");
-}
-
-# 2005-08-10 Myk Melez <myk@mozilla.org> bug 287325
-# Record each field's type and whether or not it's a custom field in fielddefs.
-$dbh->bz_add_column('fielddefs', 'type',
- { TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0 });
-$dbh->bz_add_column('fielddefs', 'custom',
- { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE' });
-
-$dbh->bz_add_column('fielddefs', 'enter_bug',
- {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
-
-# Change the name of the fieldid column to id, so that fielddefs
-# can use Bugzilla::Object easily. We have to do this up here, because
-# otherwise adding these field definitions will fail.
-$dbh->bz_rename_column('fielddefs', 'fieldid', 'id');
-
-# If the largest fielddefs sortkey is less than 100, then
-# we're using the old sorting system, and we should convert
-# it to the new one before adding any new definitions.
-if (!$dbh->selectrow_arrayref(
- 'SELECT COUNT(id) FROM fielddefs WHERE sortkey >= 100'))
-{
- print "Updating the sortkeys for the fielddefs table...\n";
- my $field_ids = $dbh->selectcol_arrayref(
- 'SELECT id FROM fielddefs ORDER BY sortkey');
- my $sortkey = 100;
- foreach my $field_id (@$field_ids) {
- $dbh->do('UPDATE fielddefs SET sortkey = ? WHERE id = ?',
- undef, $sortkey, $field_id);
- $sortkey += 100;
- }
-}
+Bugzilla::Install::DB::update_fielddefs_definition();
# Create field definitions
foreach my $definition (OLD_FIELD_DEFS) {
@@ -629,15 +593,12 @@ if ($old_field_id && ($old_field_name ne $new_field_name)) {
$query =~ s/=\Q$old_field_name\E(&|$)/=$new_field_name$1/gi;
$sth_UpdateSeries->execute($query, $series_id);
}
-
# Now that saved searches have been fixed, we can fix the field name.
print "Fixing the 'fielddefs' table...\n";
print "New field name: " . $new_field_name . "\n";
$dbh->do('UPDATE fielddefs SET name = ? WHERE id = ?',
undef, ($new_field_name, $old_field_id));
}
-
-
Bugzilla::Field::create_or_update(
{name => $new_field_name, desc => $field_description});
@@ -689,421 +650,11 @@ if (!$class_count) {
}
###########################################################################
-# Update the tables to the current definition --TABLE--
+# Update the tables to the current definition --TABLE--
###########################################################################
Bugzilla::Install::DB::update_table_definitions();
-# 2005-04-07 - alt@sonic.net, bug 289455
-# make classification_id field type be consistent with DB:Schema
-$dbh->bz_alter_column('products', 'classification_id',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => '1'});
-
-# initialowner was accidentally NULL when we checked-in Schema,
-# when it really should be NOT NULL.
-$dbh->bz_alter_column('components', 'initialowner',
- {TYPE => 'INT3', NOTNULL => 1}, 0);
-
-# 2005-03-28 - bug 238800 - index flags.type_id to make editflagtypes.cgi speedy
-$dbh->bz_add_index('flags', 'flags_type_id_idx', [qw(type_id)]);
-
-# For a short time, the flags_type_id_idx was misnamed in upgraded installs.
-$dbh->bz_drop_index('flags', 'type_id');
-
-# 2005-04-28 - LpSolit@gmail.com - Bug 7233: add an index to versions
-$dbh->bz_alter_column('versions', 'value',
- {TYPE => 'varchar(64)', NOTNULL => 1});
-$dbh->bz_add_index('versions', 'versions_product_id_idx',
- {TYPE => 'UNIQUE', FIELDS => [qw(product_id value)]});
-
-# Milestone sortkeys get a default just like all other sortkeys.
-if (!exists $dbh->bz_column_info('milestones', 'sortkey')->{DEFAULT}) {
- $dbh->bz_alter_column('milestones', 'sortkey',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
-}
-
-# 2005-06-14 - LpSolit@gmail.com - Bug 292544: only set creation_ts
-# when all bug fields have been correctly set.
-$dbh->bz_alter_column('bugs', 'creation_ts', {TYPE => 'DATETIME'});
-
-if (!exists $dbh->bz_column_info('whine_queries', 'title')->{DEFAULT}) {
-
- # The below change actually has nothing to do with the whine_queries
- # change, it just has to be contained within a schema change so that
- # it doesn't run every time we run checksetup.
-
- # Old Bugzillas have "other" as an OS choice, new ones have "Other"
- # (capital O).
- print "Setting any 'other' op_sys to 'Other'...\n";
- $dbh->do('UPDATE op_sys SET value = ? WHERE value = ?',
- undef, "Other", "other");
- $dbh->do('UPDATE bugs SET op_sys = ? WHERE op_sys = ?',
- undef, "Other", "other");
- if (Bugzilla->params->{'defaultopsys'} eq 'other') {
- # We can't actually fix the param here, because WriteParams() will
- # make $datadir/params unwriteable to the webservergroup.
- # It's too much of an ugly hack to copy the permission-fixing code
- # down to here. (It would create more potential future bugs than
- # it would solve problems.)
- print "WARNING: Your 'defaultopsys' param is set to 'other', but"
- . " Bugzilla now\n"
- . " uses 'Other' (capital O).\n";
- }
-
- # Add a DEFAULT to whine_queries stuff so that editwhines.cgi
- # works on PostgreSQL.
- $dbh->bz_alter_column('whine_queries', 'title', {TYPE => 'varchar(128)',
- NOTNULL => 1, DEFAULT => "''"});
-}
-
-# 2005-06-29 bugreport@peshkin.net, bug 299156
-if ($dbh->bz_index_info('attachments', 'attachments_submitter_id_idx')
- && (scalar(@{$dbh->bz_index_info('attachments',
- 'attachments_submitter_id_idx'
- )->{FIELDS}}) < 2)
- ) {
- $dbh->bz_drop_index('attachments', 'attachments_submitter_id_idx');
-}
-$dbh->bz_add_index('attachments', 'attachments_submitter_id_idx',
- [qw(submitter_id bug_id)]);
-
-# 2005-08-25 - bugreport@peshkin.net - Bug 305333
-if ($dbh->bz_column_info("attachments", "thedata")) {
- print "Migrating attachment data to its own table...\n";
- print "(This may take a very long time)\n";
- $dbh->do("INSERT INTO attach_data (id, thedata)
- SELECT attach_id, thedata FROM attachments");
- $dbh->bz_drop_column("attachments", "thedata");
-}
-
-# 2005-11-26 - wurblzap@gmail.com - Bug 300473
-# Repair broken automatically generated series queries for non-open bugs.
-my $broken_series_indicator =
- 'field0-0-0=resolution&type0-0-0=notequals&value0-0-0=---';
-my $broken_nonopen_series =
- $dbh->selectall_arrayref("SELECT series_id, query FROM series
- WHERE query LIKE '$broken_series_indicator%'");
-if (@$broken_nonopen_series) {
- print 'Repairing broken series...';
- my $sth_nuke =
- $dbh->prepare('DELETE FROM series_data WHERE series_id = ?');
- # This statement is used to repair a series by replacing the broken query
- # with the correct one.
- my $sth_repair =
- $dbh->prepare('UPDATE series SET query = ? WHERE series_id = ?');
- # The corresponding series for open bugs look like one of these two
- # variations (bug 225687 changed the order of bug states).
- # This depends on the set of bug states representing open bugs not to have
- # changed since series creation.
- my $open_bugs_query_base_old =
- join("&", map { "bug_status=" . url_quote($_) }
- ('UNCONFIRMED', 'NEW', 'ASSIGNED', 'REOPENED'));
- my $open_bugs_query_base_new =
- join("&", map { "bug_status=" . url_quote($_) } BUG_STATE_OPEN);
- my $sth_openbugs_series =
- $dbh->prepare("SELECT series_id FROM series
- WHERE query IN (?, ?)");
- # Statement to find the series which has collected the most data.
- my $sth_data_collected =
- $dbh->prepare('SELECT count(*) FROM series_data WHERE series_id = ?');
- # Statement to select a broken non-open bugs count data entry.
- my $sth_select_broken_nonopen_data =
- $dbh->prepare('SELECT series_date, series_value FROM series_data' .
- ' WHERE series_id = ?');
- # Statement to select an open bugs count data entry.
- my $sth_select_open_data =
- $dbh->prepare('SELECT series_value FROM series_data' .
- ' WHERE series_id = ? AND series_date = ?');
- # Statement to fix a broken non-open bugs count data entry.
- my $sth_fix_broken_nonopen_data =
- $dbh->prepare('UPDATE series_data SET series_value = ?' .
- ' WHERE series_id = ? AND series_date = ?');
- # Statement to delete an unfixable broken non-open bugs count data entry.
- my $sth_delete_broken_nonopen_data =
- $dbh->prepare('DELETE FROM series_data' .
- ' WHERE series_id = ? AND series_date = ?');
-
- foreach (@$broken_nonopen_series) {
- my ($broken_series_id, $nonopen_bugs_query) = @$_;
-
- # Determine the product-and-component part of the query.
- if ($nonopen_bugs_query =~ /^$broken_series_indicator(.*)$/) {
- my $prodcomp = $1;
-
- # If there is more than one series for the corresponding open-bugs
- # series, we pick the one with the most data, which should be the
- # one which was generated on creation.
- # It's a pity we can't do subselects.
- $sth_openbugs_series->execute($open_bugs_query_base_old . $prodcomp,
- $open_bugs_query_base_new . $prodcomp);
- my ($found_open_series_id, $datacount) = (undef, -1);
- foreach my $open_series_id ($sth_openbugs_series->fetchrow_array()) {
- $sth_data_collected->execute($open_series_id);
- my ($this_datacount) = $sth_data_collected->fetchrow_array();
- if ($this_datacount > $datacount) {
- $datacount = $this_datacount;
- $found_open_series_id = $open_series_id;
- }
- }
-
- if ($found_open_series_id) {
- # Move along corrupted series data and correct it. The
- # corruption consists of it being the number of all bugs
- # instead of the number of non-open bugs, so we calculate the
- # correct count by subtracting the number of open bugs.
- # If there is no corresponding open-bugs count for some reason
- # (shouldn't happen), we drop the data entry.
- print " $broken_series_id...";
- $sth_select_broken_nonopen_data->execute($broken_series_id);
- while (my $rowref =
- $sth_select_broken_nonopen_data->fetchrow_arrayref()) {
- my ($date, $broken_value) = @$rowref;
- my ($openbugs_value) =
- $dbh->selectrow_array($sth_select_open_data, undef,
- $found_open_series_id, $date);
- if (defined($openbugs_value)) {
- $sth_fix_broken_nonopen_data->execute
- ($broken_value - $openbugs_value,
- $broken_series_id, $date);
- }
- else {
- print "\nWARNING - During repairs of series " .
- "$broken_series_id, the irreparable data\n" .
- "entry for date $date was encountered and is " .
- "being deleted.\n" .
- "Continuing repairs...";
- $sth_delete_broken_nonopen_data->execute
- ($broken_series_id, $date);
- }
- }
-
- # Fix the broken query so that it collects correct data in the
- # future.
- $nonopen_bugs_query =~
- s/^$broken_series_indicator/field0-0-0=resolution&type0-0-0=regexp&value0-0-0=./;
- $sth_repair->execute($nonopen_bugs_query, $broken_series_id);
- }
- else {
- print "\nWARNING - Series $broken_series_id was meant to\n" .
- "collect non-open bug counts, but it has counted\n" .
- "all bugs instead. It cannot be repaired\n" .
- "automatically because no series that collected open\n" .
- "bug counts was found. You'll probably want to delete\n" .
- "or repair collected data for series $broken_series_id " .
- "manually.\n" .
- "Continuing repairs...";
- }
- }
- }
- print " done.\n";
-}
-
-# 2005-09-15 lance.larsh@oracle.com Bug 308717
-if ($dbh->bz_column_info("series", "public")) {
- # PUBLIC is a reserved word in Oracle, so renaming the column
- # PUBLIC in table SERIES avoids having to quote the column name
- # in every query against that table
- $dbh->bz_rename_column('series', 'public', 'is_public');
-}
-
-# 2005-09-28 bugreport@peshkin.net Bug 149504
-$dbh->bz_add_column('attachments', 'isurl',
- {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
-
-# 2005-10-21 LpSolit@gmail.com - Bug 313020
-$dbh->bz_add_column('namedqueries', 'query_type',
- {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
-
-# 2005-11-04 LpSolit@gmail.com - Bug 305927
-$dbh->bz_alter_column('groups', 'userregexp',
- {TYPE => 'TINYTEXT', NOTNULL => 1, DEFAULT => "''"});
-
-# 2005-09-26 - olav@bkor.dhs.org - Bug 119524
-# Convert logincookies into a varchar
-# this allows to store a random token instead of a guessable auto_increment
-$dbh->bz_alter_column('logincookies', 'cookie',
- {TYPE => 'varchar(16)', PRIMARYKEY => 1, NOTNULL => 1});
-
-# Fixup for Bug 101380
-# "Newlines, nulls, leading/trailing spaces are getting into summaries"
-
-my $controlchar_bugs =
- $dbh->selectall_arrayref("SELECT short_desc, bug_id FROM bugs WHERE " .
- $dbh->sql_regexp('short_desc', "'[[:cntrl:]]'"));
-if (scalar(@$controlchar_bugs))
-{
- my $msg = 'Cleaning control characters from bug summaries...';
- my $found = 0;
- foreach (@$controlchar_bugs) {
- my ($short_desc, $bug_id) = @$_;
- my $clean_short_desc = clean_text($short_desc);
- if ($clean_short_desc ne $short_desc) {
- print $msg if !$found;
- $found = 1;
- print " $bug_id...";
- $dbh->do("UPDATE bugs SET short_desc = ? WHERE bug_id = ?",
- undef, $clean_short_desc, $bug_id);
- }
- }
- print " done.\n" if $found;
-}
-
-# 2005-12-07 altlst@sonic.net -- Bug 225221
-$dbh->bz_add_column('longdescs', 'comment_id',
- {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
-
-# 2006-03-02 LpSolit@gmail.com - Bug 322285
-# Do not store inactive flags in the DB anymore.
-if ($dbh->bz_column_info('flags', 'id')->{'TYPE'} eq 'INT3') {
- # We first have to remove all existing inactive flags.
- if ($dbh->bz_column_info('flags', 'is_active')) {
- $dbh->do('DELETE FROM flags WHERE is_active = 0');
- }
-
- # Now we convert the id column to the auto_increment format.
- $dbh->bz_alter_column('flags', 'id',
- {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
-
- # And finally, we remove the is_active column.
- $dbh->bz_drop_column('flags', 'is_active');
-}
-
-# short_desc should not be a mediumtext, fix anything longer than 255 chars.
-if($dbh->bz_column_info('bugs', 'short_desc')->{TYPE} eq 'MEDIUMTEXT') {
- # Move extremely long summaries into a comment ("from" the Reporter),
- # and then truncate the summary.
- my $long_summary_bugs = $dbh->selectall_arrayref(
- 'SELECT bug_id, short_desc, reporter
- FROM bugs WHERE LENGTH(short_desc) > 255');
-
- if (@$long_summary_bugs) {
- print <<EOF;
-
-WARNING: Some of your bugs had summaries longer than 255 characters.
-They have had their original summary copied into a comment, and then
-the summary was truncated to 255 characters. The affected bug numbers were:
-EOF
- my $comment_sth = $dbh->prepare(
- 'INSERT INTO longdescs (bug_id, who, thetext, bug_when)
- VALUES (?, ?, ?, NOW())');
- my $desc_sth = $dbh->prepare('UPDATE bugs SET short_desc = ?
- WHERE bug_id = ?');
- my @affected_bugs;
- foreach my $bug (@$long_summary_bugs) {
- my ($bug_id, $summary, $reporter_id) = @$bug;
- my $summary_comment = "The original summary for this bug"
- . " was longer than 255 characters, and so it was truncated"
- . " when Bugzilla was upgraded. The original summary was:"
- . "\n\n$summary";
- $comment_sth->execute($bug_id, $reporter_id, $summary_comment);
- my $short_summary = substr($summary, 0, 252) . "...";
- $desc_sth->execute($short_summary, $bug_id);
- push(@affected_bugs, $bug_id);
- }
- print join(', ', @affected_bugs) . "\n\n";
- }
-
- $dbh->bz_alter_column('bugs', 'short_desc', {TYPE => 'varchar(255)',
- NOTNULL => 1});
-}
-
-if (-e "$datadir/versioncache") {
- print "Removing versioncache...\n";
- unlink "$datadir/versioncache";
-}
-
-# 2006-07-01 wurblzap@gmail.com -- Bug 69000
-$dbh->bz_add_column('namedqueries', 'id',
- {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
-if ($dbh->bz_column_info("namedqueries", "linkinfooter")) {
- # Move link-in-footer information into a table of its own.
- my $sth_read = $dbh->prepare('SELECT id, userid
- FROM namedqueries
- WHERE linkinfooter = 1');
- my $sth_write = $dbh->prepare('INSERT INTO namedqueries_link_in_footer
- (namedquery_id, user_id) VALUES (?, ?)');
- $sth_read->execute();
- while (my ($id, $userid) = $sth_read->fetchrow_array()) {
- $sth_write->execute($id, $userid);
- }
- $dbh->bz_drop_column("namedqueries", "linkinfooter");
-}
-
-# 2006-07-07 olav@bkor.dhs.org - Bug 277377
-# Add a sortkey to the classifications
-if (!$dbh->bz_column_info('classifications', 'sortkey')) {
- $dbh->bz_add_column('classifications', 'sortkey',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
-
- my $class_ids = $dbh->selectcol_arrayref('SELECT id FROM classifications ' .
- 'ORDER BY name');
- my $sth = $dbh->prepare('UPDATE classifications SET sortkey = ? ' .
- 'WHERE id = ?');
- my $sortkey = 0;
- foreach my $class_id (@$class_ids) {
- $sth->execute($sortkey, $class_id);
- $sortkey += 100;
- }
-}
-
-# 2006-07-14 karl@kornel.name - Bug 100953
-# If a nomail file exists, move its contents into the DB
-$dbh->bz_add_column('profiles', 'disable_mail',
- { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE' });
-if (-e "$datadir/nomail") {
- # We have a data/nomail file, read it in and delete it
- my %nomail;
- print "Found a data/nomail file. Moving nomail entries into DB...\n";
- open NOMAIL, '<', "$datadir/nomail";
- while (<NOMAIL>) {
- $nomail{trim($_)} = 1;
- }
- close NOMAIL;
-
- # Go through each entry read. If a user exists, set disable_mail.
- my $query = $dbh->prepare('UPDATE profiles
- SET disable_mail = 1
- WHERE userid = ?');
- foreach my $user_to_check (keys %nomail) {
- my $uid;
- if ($uid = Bugzilla::User::login_to_id($user_to_check)) {
- my $user = new Bugzilla::User($uid);
- print "\tDisabling email for user ", $user->email, "\n";
- $query->execute($user->id);
- delete $nomail{$user->email};
- }
- }
-
- # If there are any nomail entries remaining, move them to nomail.bad
- # and say something to the user.
- if (scalar(keys %nomail)) {
- print 'The following users were listed in data/nomail, but do not ' .
- 'have an account here. The unmatched entries have been moved ' .
- "to $datadir/nomail.bad\n";
- open NOMAIL_BAD, '>>', "$datadir/nomail.bad";
- foreach my $unknown_user (keys %nomail) {
- print "\t$unknown_user\n";
- print NOMAIL_BAD "$unknown_user\n";
- delete $nomail{$unknown_user};
- }
- close NOMAIL_BAD;
- }
-
- # Now that we don't need it, get rid of the nomail file.
- unlink "$datadir/nomail";
-}
-
-
-# If you had to change the --TABLE-- definition in any way, then add your
-# differential change code *** A B O V E *** this comment.
-#
-# That is: if you add a new field, you first search for the first occurrence
-# of --TABLE-- and add your field to into the table hash. This new setting
-# would be honored for every new installation. Then add your
-# bz_add_field/bz_drop_field/bz_change_field_type/bz_rename_field code above.
-# This would then be honored by everyone who updates his Bugzilla installation.
-#
-
#
# Bugzilla uses --GROUPS-- to assign various rights to its users.
#