浅谈mysql语法解析调试方法

2023年 8月 15日 86.2k 0

本文向您介绍一种利用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 NDEBUGvoid 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 0Reading a token: Next token is token SELECT_SYM (: )  # 读入SELECTShifting token SELECT_SYM (: )                        # 移进SELECTEntering 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 42Entering 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

  1. 所有自动机状态。前文提及的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

  1. 带shift/reduce,reduce/reduce冲突的状态统计:

State 27 conflicts: 2 shift/reduceState 42 conflicts: 2 shift/reduceState 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)整理如下(注:"." 位置左边,可以看做当前状态已经移进或者规约的内容):

状态编号 状态内容
0 0 $accept: . start_entry $end
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
1367 | SELECT_SYM . select_options select_item_list opt_from_clause opt_where_clause opt_group_clause opt_having_clause opt_window_clause
1013 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
1367 | SELECT_SYM select_options . select_item_list opt_from_clause opt_where_clause opt_group_clause opt_having_clause opt_window_clause

这样,我们就能够比较清晰的知道,在sql解析的每个阶段,解析器的具体状态,因此当出现语法修改错误时,就能够很容易地定位到自己规则哪一部分出现异常,进而更快速地解决问题。

Enjoy GreatSQL 🙂

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论