目标
系列第三篇里做了基本的AST遍历。
在深入做SQL中的表名列名提取前,还需要先解决第三篇里遗留的两个实用性问题,分号和大小写
分号问题
分号问题的表现是自动生成的HiveParser.java代码,只能解析单个的语句,对包含多个语句的sql文本会报错,甚至连单个语句结尾多一个分号都不行。例如这种
SELECT DISTINCT a1.c1 c2, a1.c3 c4, '' c5 FROM db2.tb2 a1 ;
还有这种
SELECT DISTINCT a1.c1 c2, a1.c3 c4, '' c5 FROM db2.tb2 a1 ;
SELECT c6 FROM tb3 ;
原因
引发这个问题的地方很好找,就在HiveParser.g里面,具体就在statement()方法对应的这条RULE上。
// starting rule
statement
: explainStatement EOF
| execStatement EOF
;
这段天书的符号体系和正则表达式很像,简单翻译成人间语言的意思是,HiveParser接受的输入都是statement,一个statement可以是一个explainStatement 加上EOF组成,也可以是execStatement加上EOF组成。explainStatement和execStatement都是有具体类型的单个语句
这个体验不符合使用hive的经验啊,最常用提交Hive语句的方式是通过hive client,它肯定是能处理多个语句的。要往下解决这个分号问题,有两个明显的方向可选
- 扩大使用hive源码的范围
- 修改HiveParser.g里的语法规则
但要怎么选呢?笔者在Hive源码里上下翻找了一通,再结合Hive实际的使用情形后有了推断。
- HiveParser.g 的语法规则只被藏在Hive执行引擎深处的代码调用,调用时的输入已经被裁剪约束到只包含单个语句
- 最常用提交Hive语句的方式是要通过hive client,它还需要被翻译到或者thrift,或者jdbc协议上的调用。
- Hive client能处理的脚本里,会存在一部分conf设置、运行参数替换这样,需要在执行hive sql前预处理的语句
有了以上几个推断,结论也很显然,处理血缘关系的需求并不需要完备的hive client处理能力,而且裁剪hive源码的工作量不太可控,所以要选择2 ,修改HiveParser.g里的语法规则
如果对1还有兴趣,可以从研究源码同目录下的ParseDriver.java类出发。
规则修订
现在的规则名为statement, 一个语句后面就是EOF结束,作为写过正则的笔者,很自然的想法是,如果能让statement这个rule匹配上多次,分号分隔一下,是不是可以?试验过程略去不说,成功的结果如下
// starting rule
statements
: statement (SEMICOLON statement )* SEMICOLON* EOF
;
statement
: explainStatement | execStatement
;
把原先statement里这个EOF去掉,两行并做一行
然后增加一个statments这个复数名字的rule,SEMICOLON是从HiveLexer.g里的定义找出的,表示分号的写法。在.g文件里的括号()
表示括号内的内容作为一个整体判断,*
的作用和正则表达式里类似,表示前面的这个整体出现0到无限次,合起来后的效果就是,一段文本里可以有多个语句,语句之间是单个分号分隔,但最末尾的语句后的分号可以省略
验证输出
测试sql语句如下
SELECT DISTINCT a1.c1 AS c2,
a1.c3 AS c4,