实战——研究一个 MySQL8.0.32 优化器的严重 Bug

背景

近日,一位群友分享了关于 MySQL8.0.32 优化器的 Bug。我在 Bug 报告网站上详细研究了此 Bug,认为比较严重,故决定与更多人分享。以下为我对此 Bug 的详细分析过程,内容较为繁琐,建议挑重点阅读。

Bug report 的描述

https://bugs.mysql.com/bug.php?id=109699

以下是翻译:

描述:以下查询在 MySQL 8.0.31(以及最新的 MySQL 5.6)中预期返回 1 行:
['❤' => 'žlutý_😀']
但自 MySQL 8.0.32 起,没有返回任何行。
复现步骤:https://pastebin.com/qghkzwTu

CREATE TABLE `employee` (<br>`id` INT UNSIGNED AUTO_INCREMENT NOT NULL,<br>`name` VARCHAR(255) DEFAULT NULL,<br>`surname` VARCHAR(255) DEFAULT NULL,<br>`retired` TINYINT(1) DEFAULT NULL,<br>PRIMARY KEY(`id`)<br>) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;<br><br>insert into `employee` (`id`, `name`, `surname`, `retired`) values (1, 'Oliver', 'Smith', 0);<br>insert into `employee` (`id`, `name`, `surname`, `retired`) values (2, 'Jack', 'Williams', 1);<br>insert into `employee` (`id`, `name`, `surname`, `retired`) values (3, 'Harry', 'Taylor', 1);<br>insert into `employee` (`id`, `name`, `surname`, `retired`) values (4, 'Charlie', 'Lee', 0);<br><br>select * from (select 'žlutý_😀' `❤`) `🚀` where `❤` = 'žlutý_😀' group by `🚀`.`❤` having (`❤` = 'žlutý_😀');<br>

如下,经过我的验证,确实如报告所述,在 8.0.32 版本中,上述查询未返回结果,并出现一个警告,提示Invalid utf8mb4 character string: 'FD5F3F'

mysql> select * from (select 'žlutý_😀' `❤`) `🚀` where `❤` = 'žlutý_😀' group by `🚀`.`❤` having (`❤` = 'žlutý_😀');<br>Empty set, 1 warning (0.00 sec)<br><br>mysql> show warnings;<br>+---------+------+--------------------------------------------+<br>| Level | Code | Message |<br>+---------+------+--------------------------------------------+<br>| Warning | 1300 | Invalid utf8mb4 character string: 'FD5F3F' |<br>+---------+------+--------------------------------------------+<br>1 row in set (0.00 sec)<br><br>mysql> select version();<br>+-----------+<br>| version() |<br>+-----------+<br>| 8.0.32 |<br>+-----------+<br>1 row in set (0.00 sec)<br>

这个 Bug 只发生在 8.0.32,我们假定这个 Bug 的引入就是内核开发人员上一次修改 sql_derived.cc 源码时引入的。那么我们可以这样调查原因。
首先,进入 sql_derived.cc 文件

然后,点击 history 查看代码历史修改记录

这里能看到谁在什么时候修改过代码。

我对 Bug 的总结

  • Bug 链接:[MySQL Bug #105969](https://bugs.mysql.com/bug.php?id=105969)
  • 官方修复代码:[MySQL Commit](https://github.com/mysql/mysql-server/commit/463608249c245c09b71aa55da3022eac95828a0f)
  • 官方修复公告:[MySQL 8.0.33 Release Notes](https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-33.html)

前面提到了,我找到和 Bug 有关的描述页面就这三个,我基于这三个页面看到的做了总结。

问题描述

在 MySQL 中,创建派生表是一个常见操作,尤其是在执行涉及 JOIN 操作或特定 WHERE 条件的复杂查询时。这个 Bug 涉及到字符集的处理问题,具体表现在:

  1. 字符集问题:
  • 在处理派生表的 WHERE 条件时,MySQL 默认使用二进制字符集,仅能正确处理 ASCII 字符集(英文字母和常见符号)。
  • 当 WHERE 条件字符串包含非 ASCII 字符时(如中文、阿拉伯文或特殊符号),MySQL 无法正确处理。
  1. 非 ASCII 字符问题:
  • 派生表无 UNION 操作:如果 WHERE 条件用到派生表列名,并且列名包含非 ASCII 字符,则会出现问题。

  • 官方给出的测试案例:

SELECT * FROM (SELECT 'å' AS x) AS dt WHERE x = 'å';<br>