MySQL Order By 注入:行为背后的原理分析
0x00 前言 时隔近两年,回头看了一下之前写的《MySQL Order By 注入的小技巧》,结尾留了一句: 至于 MySQL 的行为为何如此奇怪,本人并没有了解清楚,有待研究。 最近在 Claude Code 的帮助下终于把这个坑填上了。本文基于 MySQL 8.4 源码(tag mysql-8.4.0)、官方 Bug Tracker,以及 Docker 里跑 mysql:8.4 抓到的 optimizer_trace,把这套行为彻底捋清楚。 结论先抛在前:当年那个「直接写 sleep / GTID_SUBSET 没效果、包成子查询就能跑」并不是优化器的三条独立优化路径叠加的结果: JOIN::remove_const 在 plan_is_const() 为真时,把整段 ORDER BY 直接砍掉。 同一函数在逐项扫描时,会把 used_tables() == 0 的常量项静默丢弃,完全不调用 val_int。 子查询里的 derived table(派生表)会在优化阶段被独立物化,与外层 ORDER BY 的命运无关 —— 这才是 POC 写法稳定可用的根本原因。 Oracle 工程师 Roy Lyseng 在 Bug #67632 已经明确表态,这种「能算就提前算」的激进求值是** by design**。 0x01 重新审视现象 用 10 行整数表 rows10 和 MySQL 内置的 dual(行为上等价于一行的常量表)作为两种外层 FROM,跑下面 5 类 ORDER BY 表达式: ...