Bug #112946 INSTANT algo <= 8028 on table in mysql schema leads to corruption post upgrade
Submitted: 3 Nov 2023 2:31 Modified: 9 Feb 22:33
Reporter: Marc Reilly Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: DDL Severity:S2 (Serious)
Version:8.0.35 OS:Any
Assigned to: CPU Architecture:Any
Tags: corruption, data dictionary, INSTANT add column

[3 Nov 2023 2:31] Marc Reilly
Description:
When a pre 8029 instant column exists on a non-system table with NULL columns in the mysql schema, the table will become corrupted post 8030+ upgrade.

I have observed this behavior on all versions 8030+ when upgrading from <= 8028. 

How to repeat:
1. On 8.0.28, create two identical tables. One in the MySQL schema, another in the test schema.
CREATE TABLE `test`.`user_table` (
  `id` int AUTO_INCREMENT primary key,
  `insert_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `user` varchar(50) NOT NULL,
  `action` varchar(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `mysql`.`user_table` (
  `id` int auto_increment primary key,
  `insert_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `user` varchar(50) NOT NULL,
  `action` varchar(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2. Insert 1 row in each table
INSERT INTO test.user_table(user, action) VALUES (current_user(),'logged in');
INSERT INTO mysql.user_table(user, action) VALUES (current_user(),'logged in');

3. Add an instant column to each table:
alter table test.user_table add column `uh_oh` tinyint(1) DEFAULT NULL, algorithm=instant;
alter table mysql.user_table add column `uh_oh` tinyint(1) DEFAULT NULL, algorithm=instant;

4. After the above you will notice the following. Table data is fine, and we have instant cols as expected:
mysql [localhost:8028] {msandbox} ((none)) > select * from information_schema.innodb_tables where INSTANT_COLS>0;
+----------+------------------+------+--------+-------+------------+---------------+------------+--------------+
| TABLE_ID | NAME             | FLAG | N_COLS | SPACE | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE | INSTANT_COLS |
+----------+------------------+------+--------+-------+------------+---------------+------------+--------------+
|     1063 | test/user_table  |   33 |      8 |     2 | Dynamic    |             0 | Single     |            4 |
|     1064 | mysql/user_table |   33 |      8 |     3 | Dynamic    |             0 | Single     |            4 |
+----------+------------------+------+--------+-------+------------+---------------+------------+--------------+
2 rows in set (0.01 sec)

mysql [localhost:8028] {msandbox} ((none)) > select * from test.user_table;
+----+---------------------+--------------------+-----------+-------+
| id | insert_time         | user               | action    | uh_oh |
+----+---------------------+--------------------+-----------+-------+
|  1 | 2023-11-03 02:14:19 | msandbox@localhost | logged in |  NULL |
+----+---------------------+--------------------+-----------+-------+
1 row in set (0.00 sec)

mysql [localhost:8028] {msandbox} ((none)) > select * from mysql.user_table;
+----+---------------------+--------------------+-----------+-------+
| id | insert_time         | user               | action    | uh_oh |
+----+---------------------+--------------------+-----------+-------+
|  1 | 2023-11-03 02:14:19 | msandbox@localhost | logged in |  NULL |
+----+---------------------+--------------------+-----------+-------+
1 row in set (0.00 sec)

5. Now upgrade to any version > 8028. Here I used 8035. 

6. Post upgrade, insert two rows:

INSERT INTO test.user_table(user, action) VALUES (current_user(),'ouch');
INSERT INTO mysql.user_table(user, action) VALUES (current_user(),'ouch');

7. After insert, read the table and you will notice the table in the mysql schema becomes corrupt - note the incomplete data. The user schema table is fine though

mysql [localhost:8035] {msandbox} ((none)) > INSERT INTO test.user_table(user, action) VALUES (current_user(),'ouch');
Query OK, 1 row affected (0.01 sec)

mysql [localhost:8035] {msandbox} ((none)) > INSERT INTO mysql.user_table(user, action) VALUES (current_user(),'ouch');
Query OK, 1 row affected (0.01 sec)

mysql [localhost:8035] {msandbox} ((none)) > select * from test.user_table;
+----+---------------------+--------------------+-----------+-------+
| id | insert_time         | user               | action    | uh_oh |
+----+---------------------+--------------------+-----------+-------+
|  1 | 2023-11-03 02:14:19 | msandbox@localhost | logged in |  NULL |
|  2 | 2023-11-03 02:16:41 | msandbox@localhost | ouch      |  NULL | <--- no corruption
+----+---------------------+--------------------+-----------+-------+
2 rows in set (0.00 sec)

mysql [localhost:8035] {msandbox} ((none)) > select * from mysql.user_table;
+----+---------------------+--------------------+--------------------+-------+
| id | insert_time         | user               | action             | uh_oh |
+----+---------------------+--------------------+--------------------+-------+
|  1 | 2023-11-03 02:14:19 | msandbox@localhost | logged in          |  NULL |
|  2 | 2023-11-03 02:16:42 | m                  | sandbox@localhosto |  NULL | <---- Corrupt row
+----+---------------------+--------------------+--------------------+-------+
2 rows in set (0.00 sec)

Suggested fix:
Although its prob not a best practice to create tables in the mysql schema, any non-datadict tables should be handled correctly should a instant column be added - it should not lead to corruption, esp since instant is the default algo. Prior to instant ddl changes in 8029, for example upgrade from 8027->8028, this did not occur.

Thanks!
Marc
[6 Nov 2023 9:04] MySQL Verification Team
Hello Marc Reilly,

Thank you for the report and test case.
Verified as described.

regards,
Umesh
[6 Nov 2023 18:07] Marc Reilly
FYI: it looks like 8.2.0 has a fix for this that was not added to 8.0.35
https://github.com/mysql/mysql-server/commit/68da9a26c56c1f522483fd7b2da810c3e4ccc88f

> InnoDB: If a MySQL table in a system schema had an INSTANT ADD column that was added before 8.0.29 (they are not allowed as of that version), and after MySQL was upgraded to a version greater than 8.0.29, DMLs on these tables would result in the server unexpectedly closing.
Our thanks to Richard Dang for the contribution. (Bug #35625510)
[9 Feb 22:33] Philip Olson
Posted by developer:
 
Thank you for the bug report, the documentation was updated to include backporting this fix to 8.0.37; which was tracked via Bug #36180360.