MySQL 8.0+ 的 EXPLAIN FORMAT=TREE 输出优化前的逻辑执行树,结构上高度对应AST,但非标准可导出AST;真AST需调试源码parse_sql()获取,或用sqlglot等外部解析器替代。

MySQL 8.0+ 自带 EXPLAIN FORMAT=TREE 能看到 AST 的等效结构
MySQL 内部不暴露标准意义上的 AST(比如像 PostgreSQL 那样可导出 JSON 格式 AST),但 EXPLAIN FORMAT=TREE 输出的是解析后、优化前的逻辑执行树,它和 AST 在结构层级上高度对应——节点类型(SELECT、TABLE_SCAN、INDEX_RANGE_SCAN)、嵌套关系、条件下推位置都直接反映语法解析与语义绑定结果。
实操建议:
- 只在 MySQL 8.0 及以上版本可用;5.7 及更早版本不支持
FORMAT=TREE - 必须对真实可执行的 SQL 运行,不能是语法错误的语句(否则报错,不输出树)
- 避免带用户变量或存储过程逻辑的 SQL,它们会干扰树的纯净度
- 示例:
EXPLAIN FORMAT=TREE SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.t1_id WHERE b.status = 'active';
想看真正的解析阶段 AST?得进 MySQL 源码,盯住 parse_sql() 和 Parse_tree_root
MySQL Server 层的 SQL 解析器(基于 Bison 生成)产出的 AST 是 C++ 对象图,核心入口是 parse_sql() 函数,最终挂载到 THD::m_parser_state->m_parse_tree,类型为 Parse_tree_root。它不是供外部调用的 API,而是服务端内部数据结构。
常见错误现象:
- 试图用
SHOW WARNINGS或INFORMATION_SCHEMA查 AST → 查不到,这些接口不暴露解析树 - 在客户端连接里执行
SELECT * FROM performance_schema.xxx找 AST 表 → 不存在这类表 - 误以为
mysqlbinlog或慢日志能还原 AST → 它们只记录文本或执行摘要,不存结构
如果你真要调试 AST:
- 编译 debug 版 MySQL(带符号表),用 GDB 在
parse_sql()返回前打断点 - 打印
thd->m_parser_state->m_parse_tree成员,需配合源码理解字段含义(如m_select_lex、m_where_cond) - 注意:AST 构建发生在权限检查之前,所以即使用户无权限,只要语法合法,AST 仍会生成(但后续流程会中止)
mysql --debug-info 和 mysqld --debug=d,parser 不输出 AST,只打日志片段
很多人搜 “MySQL debug parser” 会试这两个开关,但它们的作用被严重高估了。它们不会打印 AST 结构,只会输出零散的词法/语法分析中间状态,比如 “shift token SELECT”,“reduce rule join_table”,对理解整体树形结构帮助极小。
使用场景局限:
-
--debug=d,parser日志量极大,且格式非结构化,grepSELECT_LEX或Item_func_eq可能捞到个别节点线索,但无法还原父子关系 - 日志中出现的类名(如
Item_func_eq)是语义层对象,不是原始 AST 节点;它已过了类型推导和别名绑定,比纯 AST 更“重” - 开启后性能暴跌,仅适合单语句本地调试,不可用于线上或批量分析
替代方案:用 libmysqlclient + 自定义解析器做静态分析(绕过 MySQL)
如果目标是“分析复杂 SQL 的 AST”而非“看 MySQL 怎么解析它”,更实际的路是脱离 MySQL 运行时,用独立 SQL 解析库。比如 Python 的 sqlglot 或 Go 的 vitess parser,它们能输出标准 AST(JSON/树对象),支持 MySQL 方言,并允许你遍历、修改、注入逻辑。
为什么这比啃 MySQL 源码更可行:
-
sqlglot.parse("SELECT * FROM t WHERE x > 1", dialect="mysql")直接返回 AST 对象,.find(sqlglot.expressions.Where)就能定位条件节点 - 能处理 MySQL 不支持的语法变体(如 CTE 嵌套深度超限),也兼容旧版 MySQL 不认的语法(如
VALUES ROW()) - 没有编译/调试/权限/性能门槛,脚本跑完就出结果
- 注意:这些库的 AST 和 MySQL 实际执行树仍有差异——比如它们不模拟 MySQL 的隐式类型转换规则或
STRICT_TRANS_TABLES影响
真正卡住人的地方从来不是“有没有 AST”,而是“你要 AST 干什么”。查执行计划偏差?用 EXPLAIN FORMAT=TREE。改写 SQL?用 sqlglot。定位解析崩溃原因?才需要翻 sql_yacc.yy。别让工具链的边界模糊成问题本身。










