sql 生成工具可用于测试 parser 与其他数据库产品的兼容性,通过解析 yacc 语法文件中的产生式,生成对应的 sql 语句,再使用数据库执行该 sql,根据结果判断语句是否与其他数据库语法兼容。
01 工具使用
语法文件预处理
预处理目的是将语法文件中无关的内容去除,只保留各个语句的产生式,可以通过命令 bison -v sql.y 获取语法文件中的语法规则(不带 action),然后再去除生成文件中的无用部分,如终结符列表、非终结符列表、状态转换表等,如下所示:
生成的 sql.output 文件内容如下,我们仅保留其“语法”一节
注:对于保留的“语法” 一节,还需要去除其序号。
对于上述过程,我们通过预处理脚本 preprocess.sh 进行封装,使处理后的文件满足工具的要求。生成的文件形式如下,输出的 .output 文件即为预处理后的语法文件。
sql 语句生成
生成符合条件的语法文件后,即可使用工具生成 sql。工具支持如下参数:
•-b:指定语法文件,必选。语法文件为 preprocess.sh 脚本处理后的产生的文件
•-n:指定待生成的产生式名称,必选
•-r:随机生成模式,可选,默认为枚举模式
•-o:指定生成 sql 语句的保存文件,可选,默认为 report.csv
•-n:限制生成 sql 条数,可选,默认不限制
02 工具实现
该工具包含两个 package:yacc_parser和sql_generator,分别负责完成 token 解析和 sql 生成。
产生式的表示方法
type seqinfo struct {
items []string
}
type production struct {
head string // 产生式头部
alter []seqinfo // 产生式 body
}token 解析
函数 tokenize 用于将读取的语法文件中的字符 token 化,每次调用将返回一个 token。该函数仅处理了简单的分隔符和引号,并未实现标准词法分析器的正则匹配。
parse 函数调用 tokenize 函数,每次返回一个 token,返回后 parse 函数根据当前状态和 token 类型,将一连串的 token 组装成 production。
sql 生成
sql 生成有两种模式:
1、是遍历 production 中指定产生式的 body 列表,枚举生成 sql 语句;
2、随机选择 production 中指定产生式的 body 列表,随机生成 sql 语句。
1、枚举
枚举的实现方式是使用一个链表保存待 resolve 的token,每次从链表头取一个 token,并自增该 token出现的次数,再根据其每个子表达式中 token 在记录中出现次数是否大于指定次数,筛选可以继续推导的子表达式。
另一方面使用了两个数组记录当前所取的子表达式的下标(choice)和当前最大子表达式下标(max)进行记录,以便下一次自增 choice 取下一个表达式。
经过筛选后,选取 choice 位置的产生式右部子表达式并将其全部 token 插入链表头部,然后判断头部是否为 literal 或 keyword,如果是则取出头部放入 sql 数组,如果不是则继续循环处理链表。
当处理到当前产生式末尾时(判断方式为 choice>max),此时将尝试“进位”,即记录的当前所取的位置数组最后一位自增。
比如:max 数组为 1 2 1 3,choice 数组为 0 0 0 3,则进位后 choice 数组为 0 0 1 0,表示最后一个位置已全部遍历,现在要将倒数第二位自增,最后一位置零,继续下一次排列组合的读取。
生成过程则是通过递归实现,例如针对以下这条产生式,处理逻辑如图所示:
show_tables_stmt: show tables from name '.' name with_comment * show tables from name with_comment * show tables with_comment with_comment: with comment * %empty name: ident |
根据记录的 choice 值,选择产生式的第 choice 条子表达式,直到生成一条 sql。然后再将choice数组进位,继续下一轮选择。
2、随机
随机生成模式与枚举生成模式类似,区别在于其并不会顺序遍历产生式 body 列表中每个 token,而是随机选择一个 token 作为组成 sql 的一部分。
到此这篇关于一文解读 sql 生成工具的文章就介绍到这了,更多相关sql 生成工具内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论