如何实现一个SQL解析器( 二 )


SQL示例
SELECT name FROM tab WHERE id=1001;
如何实现一个SQL解析器

文章插图
上述SQL语句 , 语义分析任务会做如下检查:
  • SQL语句中表名是否存在;
  • 字段name是否存在于表tab中;
  • WHERE条件中的id字段类型是否可以与1001进行比较操作 。
上述检查结束后 , 语义解析会生成对应的表达式供优化器去使用 。
四、 如何选择SQL解析器?在了解了解析器的核心知识点后,如何选择合适的SQL解析器来应用到我们的实际业务当中呢?下面,我们来对比一下主流的两种SQL解析器 。它们分别是ANTLR和Calcite 。
4.1 ANTLRANTLR是一款功能强大的语法分析器生成器,可以用来读取、处理、执行和转换结构化文本或者二进制文件 。在大数据的一些SQL框架里面有有广泛的应用,比如Hive的词法文件是ANTLR3写的,Presto词法文件也是ANTLR4实现的,SparkSQLambda词法文件也是用Presto的词法文件改写的,另外还有HBase的SQL工具Phoenix也是用ANTLR工具进行SQL解析的 。
使用ANTLR来实现一条SQL,执行或者实现的过程大致是这样的,实现词法文件(.g4),生成词法分析器和语法分析器,生成抽象语法树(也就是我常说的AST),然后再遍历抽象语法树,生成语义树,访问统计信息,优化器生成逻辑执行计划,再生成物理执行计划去执行 。
如何实现一个SQL解析器

文章插图
官网示例:
ANTLR表达式
assign : ID '=' expr ';' ;
如何实现一个SQL解析器

文章插图
解析器的代码类似于下面这样:
ANTLR解析器代码
如何实现一个SQL解析器

文章插图
void assign() {match(ID);match('=');expr();match(';');}4.1.1 ParserParser是用来识别语言的程序,其本身包含两个部分:词法分析器和语法分析器 。词法分析阶段主要解决的问题是关键字以及各种标识符,比如INT(类型关键字)和ID(变量标识符) 。语法分析主要是基于词法分析的结果,构造一颗语法分析数,流程大致如下:
如何实现一个SQL解析器

文章插图
因此,为了让词法分析和语法分析能够正常工作,在使用ANTLR4的时候,需要定义语法(Grammar) 。
我们可以把字符流(CharStream),转换成一棵语法分析树,字符流经过词法分析会变成Token流 。Token流再最终组装成一棵语法分析树 , 其中包含叶子节点(TerminalNode)和非叶子节点(RuleNode) 。具体语法分析树如下图所示:
如何实现一个SQL解析器

文章插图
4.1.2 GrammarANTLR官方提供了很多常用的语言的语法文件,可以进行修改后直接进行复用:https://github.com/antlr/grammars-v4
在使用语法的时候 , 需要注意以下事项:
  • 语法名称和文件名要一致;
  • 语法分析器规则以小写字母开始;
  • 词法分析器规则以大写字母开始;
  • 用'string'单引号引出字符串;
  • 不需要指定开始符号;
  • 规则以分号结束;
  • ...
4.1.3 ANTLR4实现简单计算功能下面通过简单示例 , 说明ANTLR4的用法,需要实现的功能效果如下:
ANTLR示例
1+2 => 1+2=31+2*4 => 1+2*4=91+2*4-5 => 1+2*4-5=41+2*4-5+20/5 => 1+2*4-5+20/5=8(1+2)*4 => (1+2)*4=12
如何实现一个SQL解析器

文章插图
通过ANTLR处理流程如下图所示:
如何实现一个SQL解析器

文章插图
整体来说一个原则,递归下降 。即定义一个表达式(如expr) , 可以循环调用直接也可以调用其他表达式,但是最终肯定会有一个最核心的表达式不能再继续往下调用了 。
步骤一:定义词法规则文件(CommonLexerRules.g4)
CommonLexerRules.g4
如何实现一个SQL解析器

文章插图
// 定义词法规则lexer grammar CommonLexerRules;//////// 定义词法// 匹配IDID: [a-zA-Z]+ ;// 匹配INTINT: [0-9]+;// 匹配换行符NEWLINE: '\n'('\r'?);// 跳过空格、跳格、换行符WS: [ \t\n\r]+ -> skip;//////// 运算符DIV:'/';MUL:'*';ADD:'+';SUB:'-';EQU:'=';

推荐阅读