Bug 72826

git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@1134 6955db30-a419-402b-8a0d-67ecbb4d7f56
master
vfilippov 2010-12-07 14:02:56 +00:00
parent 732dcd4dd7
commit 817cdfc525
6 changed files with 295 additions and 12 deletions

View File

@ -76,11 +76,14 @@ sub remove_from_db
$dbh->bz_start_transaction();
# Reclassify products to the default classification, if needed.
$dbh->do("UPDATE products SET classification_id=1 WHERE classification_id=?", undef, $self->id);
$dbh->do(
"UPDATE fieldvaluecontrol c, fielddefs f SET c.visibility_value_id=1".
" WHERE c.field_id=f.id AND f.visibility_field_id=? AND visibility_value_id=?",
undef, $self->field->id, $self->id
);
my @dependent_fields = map { $_->id } Bugzilla->get_fields({ visibility_field_id => $self->field->id });
if (@dependent_fields)
{
$dbh->do(
"UPDATE fieldvaluecontrol SET visibility_value_id=1 WHERE field_id IN (".
join(",", @dependent_fields).") AND visibility_value_id=?", undef, $self->id
);
}
Bugzilla::Object::remove_from_db($self);

View File

@ -96,6 +96,16 @@ sub bz_last_key {
return $last_insert_id;
}
sub bz_sequence_restart
{
my $self = shift;
my ($table, $field, $start_value) = @_;
$self->do(
'ALTER SEQUENCE '.$self->quote_identifier($table.'_'.$field.'_seq').
' RESTART WITH '.$self->quote($start_value)
);
}
sub bz_check_regexp {
my ($self, $pattern) = @_;

271
contrib/migrate.pl Normal file
View File

@ -0,0 +1,271 @@
#!/usr/bin/perl
# Alternative Bugzilla database copy script
# Correctly handles custom fields and does no foreign key removal
# See help below $USAGE = ... or run this with --help
# Lines marked with "#!" comment in the end modify the DB
use strict;
# Runs before any 'uses'
BEGIN {
my $USAGE = <<EOF;
Bugzilla database copy script
USAGE: perl $0 --from DSN -u USER -p PASSWORD [--ignore TABLE]
DSN help: see perldoc DBI, perldoc DBD::mysql, perldoc DBD::Pg
Use --ignore TABLE only for some garbage tables.
Run this from contrib/ subdirectory of your Bugzilla installation.
Apply to a virgin Bugzilla database created by checksetup.pl of same Bugzilla version
Then run checksetup.pl again to populate bugs_fulltext.
Does the following:
1) Redefine existing fields with IDs taken from old database
2) Create custom fields with IDs taken from old database using Bugzilla classes
3) Copy table data in the correct order, ignoring garbage columns
4) Copy AUTOINCREMENT (MySQL) or SEQUENCE (PostgreSQL) values
EOF
# Parse command-line arguments
my ($from, $from_user, $from_password);
my @ignore_tables;
while ($_ = shift @ARGV)
{
if ($_ eq '--from')
{
$from = shift @ARGV;
}
elsif ($_ eq '--from-user' || $_ eq '-u')
{
$from_user = shift @ARGV;
}
elsif ($_ eq '--from-password' || $_ eq '-p')
{
$from_password = shift @ARGV;
}
elsif ($_ eq '--help' || $_ eq '-h')
{
print $USAGE;
exit;
}
elsif ($_ eq '--ignore')
{
push @ignore_tables, shift @ARGV;
}
}
unless ($from && $from_user)
{
print $USAGE;
exit;
}
$from_password ||= '';
}
use lib qw(..);
use DBI;
use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Field;
use Bugzilla::Extension;
# Pre-load all extensions
$Bugzilla::extension_packages = Bugzilla::Extension->load_all();
# Tables not copied or copied manually
my %seen_tables = (
'fielddefs' => 1,
'series_data' => 1,
'bz_schema' => 1,
'attach_data' => 1,
'bugs_fulltext' => 1,
'logincookies' => 1,
'tokens' => 1,
'globalauth' => 1,
'series1' => 1,
'bug_user_map' => 1,
'old_profiles' => 1,
'qwe' => 1,
);
# Ignore more tables
$seen_tables = 1 for @ignore_tables;
my %field_exclude = (
'fieldvaluecontrol' => 'field_id',
'bugs_activity' => 'fieldid',
'profiles_activity' => 'fieldid',
);
my $from = DBI->connect('DBI:mysql:database=bugs3new', 'bugzilla3', 'bugzilla3') || die "Can't connect to source DB";
my $to = Bugzilla->dbh || die "Can't connect to destination DB";
# Get table dependencies and autoincrement columns
my $table_depends = {};
my $autoincrement = {};
my ($bz_schema) = $from->selectrow_array('SELECT schema_data FROM bz_schema');
$bz_schema =~ s/^\$VAR1 = //;
$bz_schema = eval $bz_schema;
die $@ if $@;
for my $t (keys %$bz_schema)
{
for (my $i = 0; $i < @{$bz_schema->{$t}->{FIELDS}}; $i += 2)
{
my $f = $bz_schema->{$t}->{FIELDS}->[$i+1];
if ($f->{REFERENCES})
{
$table_depends->{$t}->{$f->{REFERENCES}->{TABLE}} = 1;
}
elsif ($f->{TYPE} =~ /serial/i)
{
$autoincrement->{$t} = $bz_schema->{$t}->{FIELDS}->[$i];
}
}
}
# All tables except ones for custom fields
my @copy_tables = getseq([ sort keys %$bz_schema ], \%seen_tables, $table_depends);
my $fielddefs = $from->selectall_hashref('SELECT * FROM fielddefs', 'name');
my $fieldsnew = $to->selectall_hashref('SELECT * FROM fielddefs WHERE name IN (\''.join('\',\'', keys %$fielddefs).'\')', 'name');
for (values %$fielddefs)
{
if ($_->{custom})
{
if ($_->{type} == FIELD_TYPE_SINGLE_SELECT ||
$_->{type} == FIELD_TYPE_MULTI_SELECT)
{
push @copy_tables, $_->{name};
}
if ($_->{type} == FIELD_TYPE_MULTI_SELECT)
{
push @copy_tables, 'bug_'.$_->{name};
}
}
}
print "Redefine existing fields\n";
$to->do('DELETE FROM profiles_activity'); #!
$to->do('DELETE FROM fielddefs WHERE name IN (\''.join('\',\'', keys %$fieldsnew).'\')'); #!
insertall_hashref($to, 'fielddefs', [ map { $fielddefs->{$_} } keys %$fieldsnew ]); #!
delete $fielddefs->{$_} for keys %$fieldsnew;
my @skip_fields;
for (keys %$fielddefs)
{
if ($fielddefs->{$_}->{obsolete})
{
print "Skip field $_ (y/n)? ";
if (<STDIN> =~ /^\s*y/is)
{
push @skip_fields, $_;
next;
}
}
if (/^cf_/)
{
print "Creating field $_\n";
my $field = Bugzilla::Field->create($fielddefs->{$_}); #!
}
else
{
insertall_hashref($to, 'fielddefs', [ $fielddefs->{$_} ]); #!
}
}
for my $table (@copy_tables)
{
print "Selecting $table\n";
my $data = $from->selectall_arrayref(
'SELECT * FROM '.$from->quote_identifier($table).
' WHERE '.($field_exclude{$table} ? $field_exclude{$table}.' NOT IN ('.join(',', map { $fielddefs->{$_}->{id} } @skip_fields).')' : '1'),
{Slice=>{}}
);
print "Erasing $table\n";
$to->do('DELETE FROM '.$to->quote_identifier($table)); #!
@$data || next;
my %from_keys = %{$data->[0]};
my @to_cols = @{ $to->selectcol_arrayref(
"SELECT column_name FROM information_schema.columns".
" WHERE table_catalog=current_database() and table_schema=current_schema() and table_name=?",
undef, $table
) };
delete $from_keys{$_} for @to_cols;
for my $bad_key (keys %from_keys)
{
print "Removing column $bad_key\n";
delete $_->{$bad_key} for @$data;
}
my $maxkey = 0;
if (my $ai = $autoincrement->{$table})
{
for (@$data)
{
$maxkey = $_->{$ai} if $_->{$ai} > $maxkey;
}
}
my @buf;
my $n = 0;
my $total = @$data;
while (@$data)
{
@buf = splice @$data, 0, 1024;
insertall_hashref($to, $table, \@buf); #!
$n += @buf;
print "\rInserting $table: $n/$total...";
}
print "\n";
# Initialize auto-increment values
if (my $ai = $autoincrement->{$table})
{
$maxkey = int($maxkey+1);
if (Bugzilla->dbh->isa('Bugzilla::DB::Mysql'))
{
$to->do("ALTER TABLE `$table` AUTOINCREMENT=$maxkey"); #!
}
elsif (Bugzilla->dbh->isa('Bugzilla::DB::Pg'))
{
$to->do("ALTER SEQUENCE ${table}_${ai}_seq RESTART WITH $maxkey"); #!
}
}
}
# Insert an array of hashes into a database table
sub insertall_hashref
{
my ($dbh, $table, $rows) = @_;
return 0 unless
$dbh && $table &&
$rows && ref($rows) eq 'ARRAY' && @$rows;
my $q = substr($dbh->quote_identifier('a'), 0, 1);
my @f = keys %{$rows->[0]};
my $sql = "INSERT INTO $q$table$q ($q".join("$q,$q",@f)."$q) VALUES ".
join(',',('('.(join(',', ('?') x scalar(@f))).')') x scalar(@$rows));
my @bind = map { @$_{@f} } @$rows;
return $dbh->do($sql, undef, @bind);
}
# Get node sequence from a dependency graph
sub getseq
{
my ($seq, $seen, $dep) = @_;
my @r;
for (@$seq)
{
if (!$seen->{$_})
{
$seen->{$_} = 1;
push @r, getseq([ sort keys %{$dep->{$_} || {}} ], $seen, $dep);
push @r, $_;
}
}
return @r;
}

View File

@ -13,7 +13,7 @@ die "Please create passwords.conf" if !%$passwords;
'experimental_browser_launcher' => '*chrome',
'host' => '172.29.1.29',
'port' => 4444,
'browser_url' => 'http://bugs3.office.custis.ru',
'browser_url' => 'http://pgbugs3.office.custis.ru',
'bugzilla_installation' => '',
'bugzilla_path' => '/home/www/localhost/bugs32-up',
'master_group' => 108,

View File

@ -56,11 +56,8 @@
[% END %]
</tr>
[% END %]
<tr>
<td valign="top" colspan=4>Add a new classification</td>
<td valign="top" align="center"><a href="editclassifications.cgi?action=add">Add</a></td>
</tr>
</table>
[% PROCESS global/footer.html.tmpl %]
<p><a href="editclassifications.cgi?action=add">Add a new classification</a></p>
[% PROCESS global/footer.html.tmpl %]

View File

@ -1745,6 +1745,8 @@
group
[% ELSIF class == "Bugzilla::Product" %]
product
[% ELSIF class == "Bugzilla::Classification" %]
classification
[% ELSIF class == "Bugzilla::Search::Saved" %]
saved search
[% ELSIF ( matches = class.match('^Bugzilla::Field::Choice::(.+)') ) %]