共计 2956 个字符,预计需要花费 8 分钟才能阅读完成。
欢送来到 GreatSQL 社区分享的 MySQL 技术文章,如有疑难或想学习的内容,能够在下方评论区留言,看到后会进行解答
本文向您介绍一种利用 mysql 解析器和 bison 的调试选项进行 sql 语法解析跟踪的办法。
数据库开发过程中咱们常会遇到批改 sql 语法的需要。咱们晓得,mysql 的 sql 解析器是基于 yacc 文法,采纳 EBNF 格局进行规定形容(sql/sql_yacc.yy),并借助 bison 工具生成(sql_yacc.h, sql_yacc.cc), 所以批改 sql 语法,不可避免地要和这些 yacc 文法打交道,对 sql_yacc.yy 进行革新降级。
yacc 文法是对语法解析的高度概括,它为咱们批改解析器提供了一种优雅的形式,但与此同时当咱们遇到语句解析问题,通常比拟难间接从形象的语法规定中找到起因。侥幸的是,联合 mysql 和 bison 提供的调试工具,咱们有机会将整个语法解析的过程形象化,通过解析日志,yacc 规定和主动状态机的对应,可能比拟快地实现问题的定位。
mysql 解析器调试开关
sql/sql_yacc.yy 文件下,能够看到如下一段代码:
#ifndef NDEBUG
void turn_parser_debug_on()
{
/*
MYSQLdebug is in sql/sql_yacc.cc, in bison generated code.
Turning this option on is **VERY** verbose, and should be
used when investigating a syntax error problem only.
The syntax to run with bison traces is as follows :
- Starting a server manually :
mysqld --debug="d,parser_debug" ...
- Running a test :
mysql-test-run.pl --mysqld="--debug=d,parser_debug" ...
The result will be in the process stderr (var/log/master.err)
*/
extern int yydebug;
yydebug= 1;
}
#endif
它通知咱们,debug 版本下,在 mysqld 启动时增加 -debug=”d, parser_debug 选项,数据库服务器会为咱们输入 sql 解析的具体信息(bison traces)。
这里咱们应用一条简略的 sql 语句 SELECT 1+2*3 FROM DUAL 作为例子,看它的日志输入信息(注:’#‘号后为后增加的阐明, 非原始信息),结尾局部如下:
# 注:SQL 语句会首先被词法解析器 (LEXER) 解决,输入 'SELECT_SYM NUM + NUM * NUM FROM DUAL_SYM' 这样的序列,作为语法解析器的输入
Starting parse #语句解析开始
Entering state 0
Reading a token: Next token is token SELECT_SYM (:) # 读入 SELECT
Shifting token SELECT_SYM (:) # 移进 SELECT
Entering state 42 # 栈用于记录以后推导状况
Reading a token: Next token is token NUM (:) # 读入 NUM(第一个数字 '1' 的词法解析标记)
Reducing stack by rule 1377 (line 10001): # 在读入之前,做一次栈规约(应用的规定在 sql_yacc.yy 的 10001 行)
-> $$ = nterm select_options (:)
Stack now 0 42
Entering state 1013 # 栈规约后,进入新的状态
...
输入信息里 state 42, 1013 等信息,yacc 语法主动状态机里的状态编号,为了查看它,咱们须要应用到 bison 工具手动生成主动状态机文件。
- 主动状态机文件
应用 bison 的 -v 选项,失去语法的主动状态机文件,生成形式示例如下:
cd ${SOURCE_DIR}/sql #SOURCE_DIR 为 mysql 源码目录地位
/usr/bin/bison --name-prefix=MYSQL --yacc --warnings=all,no-yacc,no-empty-rule,no-precedence,no-deprecated --defines=${BUILD_DIR}/sql/sql_yacc.h -v sql_yacc.yy #BUILD_DIR 为用户自定的编译目录地位
执行胜利后,将在 ${SOURCE_DIR}/sql 下生成一个名为 y.output 的文件,该文件形容了 bison 依据语法规定计算得出的状态机形容文件,在文件里咱们会看到:
1. 带编号的语法规定形容。如前文提及的 rule 1377,在文件中的内容为:
1377 select_options: %empty
它示意能够将一个空的产生式规约为 select_option
2. 所有自动机状态。前文提及的 state 42,在文件中显示为:
State 42
1366 query_specification: SELECT_SYM . select_options select_item_list into_clause opt_from_clause opt_where_clause opt_group_clause opt_having_clause opt_window_clause
...
ALL shift, and go to state 1004
...
select_options go to state 1013
select_option_list go to state 1014
select_option go to state 1015
query_spec_option go to state 1016
3. 带 shift/reduce,reduce/reduce 抵触的状态统计:
State 27 conflicts: 2 shift/reduce
State 42 conflicts: 2 shift/reduce
State 220 conflicts: 2 shift/reduce
本文测试应用的是 mysql-8.0.25, 它现存的 shift/reduce 抵触总共为 66 个,mysql 不激励因为语法批改而使状态机产生任何新的抵触,因而在开发过程中须要多加留神:
/*
1. We do not accept any reduce/reduce conflicts
2. We should not introduce new shift/reduce conflicts any more.
%expect 66
*/
有了 mysql 提供的栈信息,联合 bison -v 生成的状态机文件,咱们就能够将语法解析过程中的某个具体节点的推导门路给打印进去,如咱们能够将解析器在解决完 SELECT_SYM NUM + 后,筹备读入 NUM 前的推导过程 (栈状态为:0 42 1013) 整顿如下(注:”.” 地位右边,能够看做以后状态曾经移进或者规约的内容):
这样,咱们就可能比拟清晰的晓得,在 sql 解析的每个阶段,解析器的具体状态,因而当呈现语法批改谬误时,就可能很容易地定位到本人规定哪一部分出现异常,进而更疾速地解决问题。
Enjoy GreatSQL :)
本文由博客一文多发平台 OpenWrite 公布!