diff options
Diffstat (limited to 'Bugzilla/DB/Mysql.pm')
-rw-r--r-- | Bugzilla/DB/Mysql.pm | 116 |
1 files changed, 92 insertions, 24 deletions
diff --git a/Bugzilla/DB/Mysql.pm b/Bugzilla/DB/Mysql.pm index d0b9724eb..4dd2620d3 100644 --- a/Bugzilla/DB/Mysql.pm +++ b/Bugzilla/DB/Mysql.pm @@ -32,8 +32,9 @@ use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::DB::Schema::Mysql; -use List::Util qw(max); +use List::Util qw(max any); use Text::ParseWords; +use Carp; # This is how many comments of MAX_COMMENT_LENGTH we expect on a single bug. # In reality, you could have a LOT more comments than this, because @@ -52,9 +53,7 @@ sub BUILDARGS { $dsn .= ";port=$port" if $port; $dsn .= ";mysql_socket=$sock" if $sock; - my %attrs = ( - mysql_enable_utf8 => Bugzilla->params->{'utf8'}, - ); + my %attrs = ( mysql_enable_utf8 => 1 ); return { dsn => $dsn, user => $user, pass => $pass, attrs => \%attrs }; } @@ -64,7 +63,9 @@ sub on_dbi_connected { # This makes sure that if the tables are encoded as UTF-8, we # return their data correctly. - $dbh->do("SET NAMES utf8") if Bugzilla->params->{'utf8'}; + my $charset = $class->utf8_charset; + my $collate = $class->utf8_collate; + $dbh->do("SET NAMES $charset COLLATE $collate"); # Bug 321645 - disable MySQL strict mode, if set my ($var, $sql_mode) = $dbh->selectrow_array( @@ -310,6 +311,26 @@ sub bz_setup_database { die install_string('mysql_innodb_disabled'); } + if ($self->utf8_charset eq 'utf8mb4') { + my %global = map { @$_ } @{ $self->selectall_arrayref(q(SHOW GLOBAL VARIABLES LIKE 'innodb_%')) }; + my $utf8mb4_supported + = $global{innodb_file_format} eq 'Barracuda' + && $global{innodb_file_per_table} eq 'ON' + && $global{innodb_large_prefix} eq 'ON'; + + die install_string('mysql_innodb_settings') unless $utf8mb4_supported; + + my $tables = $self->selectall_arrayref('SHOW TABLE STATUS'); + foreach my $table (@$tables) { + my ($table, undef, undef, $row_format) = @$table; + my $new_row_format = $self->default_row_format($table); + next if $new_row_format =~ /compact/i; + if (lc($new_row_format) ne lc($row_format)) { + print install_string('mysql_row_format_conversion', { table => $table, format => $new_row_format }), "\n"; + $self->do(sprintf 'ALTER TABLE %s ROW_FORMAT=%s', $table, $new_row_format); + } + } + } my ($sd_index_deleted, $longdescs_index_deleted); my @tables = $self->bz_table_list_real(); @@ -345,9 +366,6 @@ sub bz_setup_database { 'SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND ENGINE = ?', undef, $db_name, 'MyISAM'); - foreach my $should_be_myisam (Bugzilla::DB::Schema::Mysql::MYISAM_TABLES) { - @$myisam_tables = grep { $_ ne $should_be_myisam } @$myisam_tables; - } if (scalar @$myisam_tables) { print "Bugzilla now uses the InnoDB storage engine in MySQL for", @@ -520,9 +538,7 @@ sub bz_setup_database { # This kind of situation happens when people create the database # themselves, and if we don't do this they will get the big # scary WARNING statement about conversion to UTF8. - if ( !$self->bz_db_is_utf8 && !@tables - && (Bugzilla->params->{'utf8'} || !scalar keys %{Bugzilla->params}) ) - { + unless ( $self->bz_db_is_utf8 ) { $self->_alter_db_charset_to_utf8(); } @@ -559,11 +575,13 @@ sub bz_setup_database { # the table charsets. # # TABLE_COLLATION IS NOT NULL prevents us from trying to convert views. + my $charset = $self->utf8_charset; + my $collate = $self->utf8_collate; my $non_utf8_tables = $self->selectrow_array( "SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_COLLATION IS NOT NULL - AND TABLE_COLLATION NOT LIKE 'utf8%' - LIMIT 1", undef, $db_name); + AND TABLE_COLLATION != ? + LIMIT 1", undef, $db_name, $collate); if (Bugzilla->params->{'utf8'} && $non_utf8_tables) { print "\n", install_string('mysql_utf8_conversion'); @@ -580,8 +598,7 @@ sub bz_setup_database { } } - print "Converting table storage format to UTF-8. This may take a", - " while.\n"; + print "Converting table storage format to $charset (collate $collate). This may take a while.\n"; foreach my $table ($self->bz_table_list_real) { my $info_sth = $self->prepare("SHOW FULL COLUMNS FROM $table"); $info_sth->execute(); @@ -594,11 +611,11 @@ sub bz_setup_database { # If this particular column isn't stored in utf-8 if ($column->{Collation} && $column->{Collation} ne 'NULL' - && $column->{Collation} !~ /utf8/) + && $column->{Collation} ne $collate) { my $name = $column->{Field}; - print "$table.$name needs to be converted to UTF-8...\n"; + print "$table.$name needs to be converted to $charset (collate $collate)...\n"; # These will be automatically re-created at the end # of checksetup. @@ -618,7 +635,7 @@ sub bz_setup_database { my ($binary, $utf8) = ($sql_def, $sql_def); my $type = $self->_bz_schema->convert_type($col_info->{TYPE}); $binary =~ s/(\Q$type\E)/$1 CHARACTER SET binary/; - $utf8 =~ s/(\Q$type\E)/$1 CHARACTER SET utf8/; + $utf8 =~ s/(\Q$type\E)/$1 CHARACTER SET $charset COLLATE $collate/; push(@binary_sql, "MODIFY COLUMN $name $binary"); push(@utf8_sql, "MODIFY COLUMN $name $utf8"); } @@ -639,7 +656,7 @@ sub bz_setup_database { print "Converting the $table table to UTF-8...\n"; my $bin = "ALTER TABLE $table " . join(', ', @binary_sql); my $utf = "ALTER TABLE $table " . join(', ', @utf8_sql, - 'DEFAULT CHARACTER SET utf8'); + "DEFAULT CHARACTER SET $charset COLLATE $collate"); $self->do($bin); $self->do($utf); @@ -649,7 +666,7 @@ sub bz_setup_database { } } else { - $self->do("ALTER TABLE $table DEFAULT CHARACTER SET utf8"); + $self->do("ALTER TABLE $table DEFAULT CHARACTER SET $charset COLLATE $collate"); } } # foreach my $table (@tables) @@ -660,7 +677,7 @@ sub bz_setup_database { # a mysqldump.) So we have this change outside of the above block, # so that it just happens silently if no actual *table* conversion # needs to happen. - if (Bugzilla->params->{'utf8'} && !$self->bz_db_is_utf8) { + unless ($self->bz_db_is_utf8) { $self->_alter_db_charset_to_utf8(); } @@ -739,18 +756,69 @@ sub _fix_defaults { } } +sub utf8_charset { + return 'utf8' unless Bugzilla->params->{'utf8'}; + return Bugzilla->params->{'utf8'} eq 'utf8mb4' ? 'utf8mb4' : 'utf8'; +} + +sub utf8_collate { + my $charset = utf8_charset(); + if ($charset eq 'utf8') { + return 'utf8_general_ci'; + } + elsif ($charset eq 'utf8mb4') { + return 'utf8mb4_unicode_520_ci'; + } + else { + croak "invalid charset: $charset"; + } +} + +sub default_row_format { + my ($class, $table) = @_; + my $charset = utf8_charset(); + if ($charset eq 'utf8') { + return 'Compact'; + } + elsif ($charset eq 'utf8mb4') { + my @no_compress = qw( + bug_user_last_visit + cc + email_rates + logincookies + token_data + tokens + ts_error + ts_exitstatus + ts_funcmap + ts_job + ts_note + user_request_log + votes + ); + return 'Dynamic' if any { $table eq $_ } @no_compress; + return 'Compressed'; + } + else { + croak "invalid charset: $charset"; + } +} + sub _alter_db_charset_to_utf8 { my $self = shift; my $db_name = Bugzilla->localconfig->{db_name}; - $self->do("ALTER DATABASE $db_name CHARACTER SET utf8"); + my $charset = $self->utf8_charset; + my $collate = $self->utf8_collate; + $self->do("ALTER DATABASE $db_name CHARACTER SET $charset COLLATE $collate"); } sub bz_db_is_utf8 { my $self = shift; - my $db_collation = $self->selectrow_arrayref( + my $db_charset = $self->selectrow_arrayref( "SHOW VARIABLES LIKE 'character_set_database'"); # First column holds the variable name, second column holds the value. - return $db_collation->[1] =~ /utf8/ ? 1 : 0; + my $charset = $self->utf8_charset; + return $db_charset->[1] eq $charset ? 1 : 0; } |