Bug #94187 Downgrade minor 5.7 version leaves incorrect innodb_table_stats schema
Submitted: 4 Feb 2019 9:14 Modified: 4 Feb 2019 13:12
Reporter: Przemyslaw Malkowski Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Command-line Clients Severity:S3 (Non-critical)
Version:5.7.21 OS:Any
Assigned to: CPU Architecture:Any

[4 Feb 2019 9:14] Przemyslaw Malkowski
Description:
After performing minor 5.7 version downgrade, the mysql_upgrade does not correct the `mysql`.`innodb_table_stats` structure.
Tested downgrade from 5.7.24 and 5.7.25 to 5.7.21. 
The tool reported as if the table was OK:

[root@dbc ~]# mysql_upgrade -p***
mysql_upgrade: [Warning] Using a password on the command line interface can be insecure.
Checking if update is needed.
Checking server version.
Running queries to upgrade MySQL server.
Checking system database.
mysql.columns_priv                                 OK
mysql.db                                           OK
mysql.engine_cost                                  OK
mysql.event                                        OK
mysql.func                                         OK
mysql.general_log                                  OK
mysql.gtid_executed                                OK
mysql.help_category                                OK
mysql.help_keyword                                 OK
mysql.help_relation                                OK
mysql.help_topic                                   OK
mysql.innodb_index_stats                           OK
mysql.innodb_table_stats                           OK
mysql.ndb_binlog_index                             OK
mysql.plugin                                       OK
mysql.proc                                         OK
mysql.procs_priv                                   OK
mysql.proxies_priv                                 OK
mysql.server_cost                                  OK
mysql.servers                                      OK
mysql.slave_master_info                            OK
mysql.slave_relay_log_info                         OK
mysql.slave_worker_info                            OK
mysql.slow_log                                     OK
mysql.tables_priv                                  OK
mysql.time_zone                                    OK
mysql.time_zone_leap_second                        OK
mysql.time_zone_name                               OK
mysql.time_zone_transition                         OK
mysql.time_zone_transition_type                    OK
mysql.user                                         OK
The sys schema is already up to date (version 1.5.1).
Checking databases.
sys.sys_config                                     OK
test.t1                                            OK
Upgrade process completed successfully.
Checking if update is needed.

But the table was not corrected and MySQL keeps complaining about that one:

2019-02-04T08:57:17.588067Z 4 [ERROR] InnoDB: Column table_name in table `mysql`.`innodb_table_stats` is VARCHAR(597) NOT NULL but should be VARCHAR(192) NOT NULL (length mismatch).
2019-02-04T08:57:17.588102Z 4 [ERROR] InnoDB: Fetch of persistent statistics requested for table `sys`.`sys_config` but the required system tables mysql.innodb_table_stats and mysql.innodb_index_stats are not present or have unexpected structure. Using transient stats instead.

In 5.7.24+, the table is:

mysql> SHOW CREATE TABLE `mysql`.`innodb_table_stats`\G
*************************** 1. row ***************************
       Table: innodb_table_stats
Create Table: CREATE TABLE `innodb_table_stats` (
  `database_name` varchar(64) COLLATE utf8_bin NOT NULL,
  `table_name` varchar(199) COLLATE utf8_bin NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `n_rows` bigint(20) unsigned NOT NULL,
  `clustered_index_size` bigint(20) unsigned NOT NULL,
  `sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`database_name`,`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0
1 row in set (0.00 sec)

In 5.7.21, it was:

mysql> SHOW CREATE TABLE `mysql`.`innodb_table_stats`\G
*************************** 1. row ***************************
       Table: innodb_table_stats
Create Table: CREATE TABLE `innodb_table_stats` (
  `database_name` varchar(64) COLLATE utf8_bin NOT NULL,
  `table_name` varchar(64) COLLATE utf8_bin NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `n_rows` bigint(20) unsigned NOT NULL,
  `clustered_index_size` bigint(20) unsigned NOT NULL,
  `sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`database_name`,`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0
1 row in set (0.00 sec)

Therefore, after downgrade, it needs to be fixed manually, which is not mentioned in the documentation:
https://dev.mysql.com/doc/refman/5.7/en/downgrading-to-previous-series.html

How to repeat:
# yum install mysql-community-server.x86_64
# systemctl start mysqld
# mysql -p
(change password)
# yum downgrade mysql-community-server-5.7.21 mysql-community-common-5.7.21 mysql-community-client-5.7.21 mysql-community-libs-5.7.21

# mysql_upgrade -p
mysql> create database test;
mysql> create table test.t1 (id int);

# tail /var/log/mysqld.log

Suggested fix:
Make sure the mysql_upgrade tool will check and correct every system table, so that downgrade path works as expected.
For old versions where it is not the case, update the downgrade notes accordingly what needs to be done manually.
[4 Feb 2019 9:58] MySQL Verification Team
Hello Przemyslaw,

Thank you for the report and feedback.
Observed with binary tarballs and in-Place downgrade procedure i.e from https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/downgrade-binary-package.html#...

regards,
Umesh
[4 Feb 2019 13:12] Przemyslaw Malkowski
Interestingly, with tables with long enough names, which are partitioned, there may be a problem with fixing the table manually with an ALTER:

mysql> alter table `mysql`.`innodb_table_stats` modify `table_name` varchar(64) COLLATE utf8_bin NOT NULL;
ERROR 1406 (22001): Data too long for column 'table_name' at row 5

So, it may be necessary to drop/truncate and re-create/alter both tables `mysql`.`innodb_table_stats` and `mysql`.`innodb_index_stats` later.

However, after downgrading, even in 5.7.21 and varchar(64), automatically filled statistics somehow manage to fit for the long name partitioned tables just fine :)

mysql> show create table mysql.innodb_table_stats\G
*************************** 1. row ***************************
       Table: innodb_table_stats
Create Table: CREATE TABLE `innodb_table_stats` (
  `database_name` varchar(64) COLLATE utf8_bin NOT NULL,
  `table_name` varchar(64) COLLATE utf8_bin NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `n_rows` bigint(20) unsigned NOT NULL,
  `clustered_index_size` bigint(20) unsigned NOT NULL,
  `sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`database_name`,`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0
1 row in set (0.00 sec)

mysql> select table_name,length(table_name) from mysql.innodb_table_stats;
+-----------------------------------------------------------------------+--------------------+
| table_name                                                            | length(table_name) |
+-----------------------------------------------------------------------+--------------------+
| myverylongnametable_myverylongnametable_myverylongnametable_myv       |                 63 |
| myverylongnametable_myverylongnametable_myverylongnametable_myve#P#p1 |                 69 |
| myverylongnametable_myverylongnametable_myverylongnametable_myve#P#p2 |                 69 |
| myverylongnametable_myverylongnametable_myverylongnametable_myve#P#p3 |                 69 |
| t1                                                                    |                  2 |
+-----------------------------------------------------------------------+--------------------+
5 rows in set (0.00 sec)