[MDEV-30483] After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL" Created: 2023-01-27 Updated: 2023-06-08 Resolved: 2023-05-25 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Storage Engine - InnoDB, Upgrades |
| Affects Version/s: | 10.4, 10.6.11, 10.5, 10.6, 10.7, 10.8, 10.9, 10.10, 10.11 |
| Fix Version/s: | 10.6.15, 10.9.8, 10.10.6, 10.11.5, 11.0.3, 11.1.2 |
| Type: | Bug | Priority: | Critical |
| Reporter: | Adam | Assignee: | Alexander Barkov |
| Resolution: | Fixed | Votes: | 2 |
| Labels: | None | ||
| Issue Links: |
|
||||||||||||
| Description |
|
After upgrade from Mysql 5.7 to MariaDB 10.3 then to 10.6, I'm now seeing the following error in the logs periodically: "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL" The last_update column appears to be correctly defined (TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP). Running mysql_upgrade does not resolve the issue,
|
| Comments |
| Comment by Elena Stepanova [ 2023-01-27 ] | ||||||||||||||||||||||
|
Thanks for the report. First of all, the InnoDB message is really misleading, because in fact the column is TIMESTAMP, both in MySQL and MariaDB. Probably a different internal format of timestamp. I cannot reproduce it if in the described scenario I run mysql_upgrade on 10.3 before further upgrading to 10.6; but otherwise, starting from 10.4, it is easily reproducible. I suppose the reason is that while mysql_upgrade actually runs an unconditional ALTER on the table
even if the columns are already of the same type, in 10.3 it works via table-copy and thus upgrades it, while in 10.4+ it remains essentially a no-op. If so, then
should help as a workaround, to get rid of the error messages. Possibly server restart will also be needed. | ||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-03-09 ] | ||||||||||||||||||||||
|
I reproduced this with a copy of a MySQL 5.7 data directory that is attached to For the first ALTER TABLE, the ha_innobase::commit_inplace_alter_table() would do nothing, because ha_alter_info->handler_flags=ALTER_COLUMN_DEFAULT, a flag that InnoDB does not care about.
| ||||||||||||||||||||||
| Comment by Otto Kekäläinen [ 2023-03-11 ] | ||||||||||||||||||||||
|
thirumarko If you give some pointers on how this should be fixed and how PRTYPE is supposed to work in upgrades, Christianggm could take a stab at fixing this. He has already sent fixes to other bugs in `mariadb-upgrade` before that you merged. | ||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-03-11 ] | ||||||||||||||||||||||
|
otto and Christianggm, the .frm data type should be mapped to an InnoDB mtype in get_innobase_type_from_mysql_type(). The prtype should be determined by its callers, such as create_table_info_t::create_table_def(). I did not dig deeper into this, but I suspect that some logic in sql/sql_alter.cc fails to take this conversion into account. For string data types, a special interface was defined to allow metadata-only conversion of compatible string encodings. Its latest revision is called ha_innobase::can_convert_nocopy(). I suspect that something in the data type compatibility checks in the SQL layer was relaxed too much, or this function needs to be called (and account for) other data types as well. One thing worth noting is that in InnoDB, signed integer types use a modified two’s complement notation where the sign bit is inverted, to allow memcmp() to sort negative values first. InnoDB uses big-endian storage format. I think that BINARY(4) should be compatible with INT UNSIGNED. That is, a metadata-only conversion is definitely possible and desirable. | ||||||||||||||||||||||
| Comment by Christian Gonzalez [ 2023-03-16 ] | ||||||||||||||||||||||
|
I've submitted a PR (https://github.com/MariaDB/server/pull/2555) to address this failure at scripts/mysql_system_tables_fix.sql using the force flag as marko did in previous comments. So at least mysql_upgrade tool can ensure that the PRTYPE is being properly updated for this specific case until we figure out and fix the real cause of the issue. | ||||||||||||||||||||||
| Comment by Otto Kekäläinen [ 2023-03-18 ] | ||||||||||||||||||||||
|
This issue is assigned to thiru - could you please review https://github.com/MariaDB/server/pull/2555 so we can merge the fix and close this issue? | ||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-03-21 ] | ||||||||||||||||||||||
|
That pull request looks more like a work around than a fix. | ||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-03-21 ] | ||||||||||||||||||||||
|
I think that we must fully understand the reason of the failure. In hexadecimal notation, the prtypes in question are 0x80707 and 0x80507. The most significant hexadecimal digit refers to my_charset_latin1.number==8, which actually does not matter at all here. The difference is in the DATA_UNSIGNED flag (0x200). I tried the following:
This would fail with ER_ALTER_OPERATION_NOT_SUPPORTED_REASON (1846): ALGORITHM=INSTANT is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY. Apparently, this problem is specific to the internal implementation of the TIMESTAMP data type. Possibly, mysql56_temporal_format (see I see that bar has changed something related to timestamps in the past. I think that he should best explain what is going on here, how to reproduce the bug in the regression test suite and how to fix it. | ||||||||||||||||||||||
| Comment by Christian Gonzalez [ 2023-03-29 ] | ||||||||||||||||||||||
|
After further investigation, it looks like the PRTYPE for the last_update field at both innodb_table_stats and innodb_index_stats tables was being properly updated for MariaDB 10.6.10, so it looks like some kind of regression was included on MariaDB 10.6.11. | ||||||||||||||||||||||
| Comment by Alexander Barkov [ 2023-04-17 ] | ||||||||||||||||||||||
|
It does not look like a regression introduces in 10.6.11. Tried with MariaDB-10.6.10:
as follows:
| ||||||||||||||||||||||
| Comment by Alexander Barkov [ 2023-04-17 ] | ||||||||||||||||||||||
|
Reassigning to marko, as the problem resides in this code in innobase/dict/dict0stats.c:
The data type for last_update should be DATA_FIXBINARY. However, it's not clear what to do on installations with @@global.mysql56_temporal_format set to 0. | ||||||||||||||||||||||
| Comment by Christian Gonzalez [ 2023-04-17 ] | ||||||||||||||||||||||
|
Hi bar maybe I'm missing something, but it seems that when you were trying to reproduce the issue you just altered the field for innodb_table_stats:
But the errors you're getting are referencing innodb_index_stats. It looks like both of them needs to be updated (ref). | ||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-04-28 ] | ||||||||||||||||||||||
|
Theoretically, we could relax the schema check (which was refactored in 10.6) and accept both 32-bit formats. We would need to debug and understand what happens with each value of @@global.mysql56_temporal_format, and to check which internal representations are compatible. We might also simply ignore differences in the timestamp column definitions and on mismatch, refrain from updating the columns. When InnoDB internally updates the statistics tables in the background, it will use the InnoDB internal SQL interpreter, which does not implement or support any DEFAULT or ON UPDATE clauses. | ||||||||||||||||||||||
| Comment by Alexander Barkov [ 2023-05-24 ] | ||||||||||||||||||||||
|
Hello marko, https://github.com/MariaDB/server/commit/fdd338fc735697d2def4c19dd92dedfd3a7bef85 Thanks! | ||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-05-24 ] | ||||||||||||||||||||||
|
OK to push after fixing the link issue related to the experimental data type plugin that you added for mimicing the MySQL 5.7 signed timestamp type. The InnoDB fix simply makes the InnoDB schema check ignore the DATA_UNSIGNED flag differences for the timestamp column. When the DATA_UNSIGNED flag is missing, InnoDB will invert the sign bit both when reading and writing the data, so that comparisons of signed integers stored in big-endian format can use straight memcmp(). According to bar, the timestamps are 31-bit unsigned time_t (never before the Unix epoch of January 1970). Therefore, the sign bit in InnoDB should always be the same for all rows of a given table. Even if timestamps before 1970 were allowed in the statistics tables, this should not matter, because those columns are not indexed and therefore no other than equality comparisons inside InnoDB should take place. | ||||||||||||||||||||||
| Comment by Alexander Barkov [ 2023-05-26 ] | ||||||||||||||||||||||
|
According to Marko, InnoDB stores last_update column values (of the two mentioned statistical tables) directly to InnoDB record buffer without using Field methods. Values are stored directly to the InnoDB record using big-endian format. These scripts prove that this should work with both:
SQL layer, which used Field methods, also always produces big-engian values, no matter what mysql56_temporal_format is:
| ||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2023-05-26 ] | ||||||||||||||||||||||
|
As discussed, I think that it is a good idea to extend the regression test suite with some validation of the contents of last_update. |