欢迎来到 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 发布!

技术分享 | 浅谈mysql语法解析调试方法的更多相关文章

  1. 技术分享 | 浅谈MySQL闪回的实现

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 1.闪回实现原理 2.binlog文件格式初探 3.闪回实现过程 1.闪回实现原 ...

  2. 浅谈mysql主从复制的高可用解决方案

    1.熟悉几个组件(部分摘自网络)1.1.drbd     —— DRBD(Distributed Replicated Block Device),DRBD号称是 "网络 RAID" ...

  3. 浅谈mysql innodb缓存策略

    浅谈mysql innodb缓存策略: The InnoDB Buffer Pool Innodb 持有一个存储区域叫做buffer pool是为了在内存中缓存数据和索引,知道innodb buffe ...

  4. 浅谈mysql配置优化和sql语句优化【转】

    做优化,我在这里引用淘宝系统分析师蒋江伟的一句话:只有勇于承担,才能让人有勇气,有承担自己的错误的勇气.有承担错误的勇气,就有去做事得勇气.无论做什么事,只要是对的,就要去做,勇敢去做.出了错误,承担 ...

  5. 浅谈MySQL中优化sql语句查询常用的30种方法 - 转载

    浅谈MySQL中优化sql语句查询常用的30种方法 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中使 ...

  6. 浅谈Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景

    浅谈Mysql共享锁.排他锁.悲观锁.乐观锁及其使用场景   Mysql共享锁.排他锁.悲观锁.乐观锁及其使用场景 一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁 ...

  7. 浅谈 js 正则之 test 方法

    原文:浅谈 js 正则之 test 方法 其实我很少用这个,所以之前一直没注意这个问题,自从落叶那厮写了个变态的测试我才去看了下这东西.先来看个东西吧. var re = /\d/; console. ...

  8. [原创]浅谈H5页面性能优化方法

    [原创]浅谈H5页面性能优化方法 前阶段公司H5页面性能测试,其中测试时也发现了一些性能瓶颈问题,接下来我们在来谈谈H5页面性能优化,仅仅是一些常用H5页面性能优化措施,其实和Web页面性能优化思路大 ...

  9. 【转】浅谈Java中的hashcode方法(这个demo可以多看看)

    浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native i ...

随机推荐

  1. 好客租房48-组件的props(基本使用)

    组件是封闭的 要接受外部数据应该通过props来实现 props的作用:接受传递给组件的数据 传递数据:给组件标签添加属性 接收数据:函数组件通过参数props接收数据 类组件通过this.props ...

  2. 使用Nginx中遇到的一个小问题思考

    我们知道在现在的网站开发中,随着请求量的快速增长,我们经常会用到负载均衡 以便使用多个网站共同支撑网络的请求,为了能让请求按照一定的规律分配给各个支撑服务器,我们会使用一些负载均衡来对请求进行分发 最 ...

  3. 《Unix 网络编程》11:名字和地址转换

    名字和地址转换 系列文章导航:<Unix 网络编程>笔记 域名系统 简介 域名系统主要用于主机名字和 IP 地址之间的映射.主机名可以是: 简单名字,如:centos01 全限定域名(FQ ...

  4. Spring Ioc源码分析系列--Bean实例化过程(二)

    Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...

  5. 深度学习与CV教程(6) | 神经网络训练技巧 (上)

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...

  6. 《HALCON数字图像处理》第六章笔记

    目录 第六章 图像增强 图像增强的概念和分类 灰度变换 直方图处理 图像的平滑 图像的锐化 图像的彩色增强 我在Gitee上建了个仓库,会将学习书本的时候打的一些代码上传上去,笔记中所有代码都在仓库里 ...

  7. BUUCTF-你竟然赶我走

    你竟然赶我走 首先看到这个图片没啥感觉,直接用16进制打开了.拖到最下面果然有flag flag{stego_is_s0_bor1ing}

  8. SAP Word97 Intergration

    *&---------------------------------------------------------------------* *& Report SAPRDEMOW ...

  9. awk内置函数、外部变量

    外部变量 ①获取外部变量 格式: awk '{action}' 变量名=变量值 ,这样传入变量可以在action中获得值. 示例: test='awk test'--day-5 外部变量 ①获取外部变 ...

  10. bat-静默安装winrar并设置系统级环境变量

    @echo off Setlocal enabledelayedexpansion @REM vscode中自动开启延迟环境变量扩展 echo 安装winrar "winrar v.5.71 ...