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


如何实现一个SQL解析器

文章插图
执行代码 , 最终输出结果如下图所示:
如何实现一个SQL解析器

文章插图
4.2 Calcite上述ANTLR内容演示了词法分析和语法分析的简单流程,但是由于ANTLR要实现SQL查询 , 需要自己定义词法和语法相关文件 , 然后再使用ANTLR的插件对文件进行编译,然后再生成代码(与Thrift的使用类似,也是先定义接口,然后编译成对应的语言文件 , 最后再继承或者实现这些生成好的类或者接口) 。
4.2.1 原理及优势而Apache Calcite的出现,大大简化了这些复杂的工程 。Calcite可以让用户很方便的给自己的系统套上一个SQL的外壳,并且提供足够高效的查询性能优化 。
  • query language;
  • query optimization;
  • query execution;
  • data management;
  • data storage;
上述这五个功能,通常是数据库系统包含的常用功能 。Calcite在设计的时候就确定了自己只关注绿色的三个部分 , 而把下面数据管理和数据存储留给各个外部的存储或计算引擎 。
数据管理和数据存储,尤其是数据存储是很复杂的 , 也会由于数据本身的特性导致实现上的多样性 。Calcite抛弃这两部分的设计,而是专注于上层更加通用的模块,使得自己能够足够的轻量化 , 系统复杂性得到控制,开发人员的精力也不至于耗费的太多 。
同时 , Calcite也没有重复去早轮子,能复用的东西,都是直接拿来复用 。这也是让开发者能够接受去使用它的一个原因 。比如,如下两个例子:
  • 例子1:作为一个SQL解析器,关键的SQL解析,Calcite没有重复造轮子,而是直接使用了开源的JavaCC,来将SQL语句转化为Java代码,然后进一步转化成一棵抽象语法树(AST)以供下一阶段使用;
  • 例子2:为了支持后面会提到的灵活的元数据功能,Calcite需要支持运行时编译Java代码 。默认的JavaC太重 , 需要一个更轻量级的编译器 , Calcite同样没有选择造轮子,而是使用了开源了Janino方案 。

如何实现一个SQL解析器

文章插图
上面的图是Calcite官方给出的架构图,从图中我们可以获取到的信息是 , 一方面印证了我们上面提到的,Calcite足够的简单,没有做自己不该做的事情;另一方面,也是更重要的,Calcite被设计的足够模块化和可插拔 。
  • 【JDBC Client】:这个模块用来支持使用JDBC Client的应用;
  • 【SQL Parser and Validator】:该模块用来做SQL解析和校验;
  • 【Expressions Builder】:用来支持自己做SQL解析和校验的框架对接;
  • 【Operator Expressions】:该模块用来处理关系表达式;
  • 【Metadata Provider】:该模块用来支持外部自定义元数据;
  • 【Pluggable Rules】:该模块用来定义优化规则;
  • 【Query Optimizer】:最核心的模块 , 专注于查询优化 。
功能模块的划分足够合理,也足够独立,使得不用完整集成,而是可以只选择其中的一部分使用,而基本上每个模块都支持自定义,也使得用户能够更多的定制系统 。
如何实现一个SQL解析器

文章插图
上面列举的这些大数据常用的组件都Calcite均有集成,可以看到Hive就是自己做了SQL解析,只使用了Calcite的查询优化功能 。而像Flink则是从解析到优化都直接使用了Calcite 。
上面介绍的Calcite集成方法,都是把Calcite的模块当做库来使用 。如果觉得太重量级,可以选择更简单的适配器功能 。通过类似Spark这些框架里自定义的Source或Sink的方式,来实现和外部系统的数据交互操作 。
如何实现一个SQL解析器

文章插图
上图就是比较典型的适配器用法,比如通过Kafka的适配器就能直接在应用层通过SQL,而底层自动转换成Java和Kafka进行数据交互(后面部分有个案例操作) 。
4.2.2 Calcite实现KSQL查询Kafk参考了EFAK(原Kafka Eagle开源项目)的SQL实现,来查询Kafka中Topic里面的数据 。
1.常规SQL查询
SQL查询
select * from video_search_query where partition in (0) limit 10
如何实现一个SQL解析器

文章插图
预览截图:

推荐阅读