Calcite 在大数据系统中有着广泛的运用,比如 Apache Flink, Apache Drill 等都大量使用了 Calcite,理解 Calcite 的原理可以说已经成为理解大数据系统中 SQL 访问层实现原理的必备条件之一。
但是不少人在学习 Calcite 的过程中都发现关于 Calcite 的实践案例其实很少,本文就将为大家详细介绍如何基于 Calcite 框架的 SQL 语法扩展探索使之更符合你的业务需求,以及扩展 SQL 在数栈产品的应用实践。
Calcite 介绍及用途
Calcite 介绍
Apache Calcite 是一个动态的数据管理框架,本身不涉及任何物理存储信息,而是专注在 SQL 解析、基于关系代数的查询优化,通过扩展方式来对接底层存储。
目前 Apache Calcite 被应用在广泛的数据开源系统中,比如 Apache Hive、Apache Phoenix、Apache Flink 等。
Calcite 的用途
Calcite 提供了 ANSI 标准 SQL 的解析,以及各种 SQL 方言,针对来自于不同数据源的复杂 SQL,在 Calcite 中会把 SQL 解析成 SqlNode 语法树结构,然后根据得到的语法树转换成自定义 Node,通过自定义 Node 解析获取到表的字段信息、以及表信息、血缘等相关信息。
下图展示了一部分对外提供的接口信息:
sqlparser 解析模块主要提供了以下几种功能 :
・解析 SQL 包含的所有表、字段信息
・解析 SQL 的 udf 函数
・解析 SQL 的血缘信息,包括表级血缘、字段血缘
・解析自定义 SqlNode
・api 服务变量解析替换
SQL 语法扩展
了解完 Calcite 是什么以及用途后,下面为大家分享 Calcite SQL 语法扩展的相关内容。
SQL 语法扩展背景
在 sqlparser 中进行 sql 解析的场景中,有两种情况需要使用到自定义扩展,一是 Calcite 不支持的一些语法;二是在一些场景中存在 sql 中带有 ${var} 自定义变量语法。
那么针对上面的这两种情况,Calcite 的自定义扩展是如何实现的呢?自定义扩展主要涉及到以下三个文件:
・Parser.jj:Parser.jj 是一个 Calcite 核心的语法和词法文件,基于 Apache FreeMaker 模版,该模版包含着变量,这些变量在编译时可以被替换
・parserImpl.ftl:提供自定义 SQL 语句、literals、dataType 的实现方法
・config.fmpp:该文件是 FMPP 的配置文件,提供了 SQL 语句、literals、dataType 的接口扩展入口
Calcite 使用 javacc 作为语法解析器,freemaker 作为模版,把 parserImpls.ftl、config.fmpp、Parser.jj 模版合成最终的语法词法文件,最终通过 javacc 编译成自定义的解析器源码,整体流程如下图所示:
扩展 SQL 实现
● 工程目录
● 扩展 sql 实现案例
支持以下 limit 相关语法以及数字可以写成 ${var} 形式:
-> limit count, limit start count
-> limit count offset start
-> offset start limit count
在原生的 Calcite 解析是支持 limit count 语法的,但是由于返回 SqlOrderBy 对象内部类 Operator 的 unparse 方法在 SQL 输出过程中对原始 SQL 进行了改写,因此需要使用扩展 SQL 得到正确的 SQL。
下面介绍一个 limit offset 语法扩展样例,扩展 SQL 如下:
select id, name from test where id > 3 order by id desc limit 1 offset ${offset_val}
整体流程如下:
01
Parser.jj 定义 ${var} 变量的 token 词法 DOLLAR_VARIABLE:
Parser.jj 扩展的变量方法接入,下面方法会在解析到 limit、offset 关键字后面的一个词时进行调用:
Parser.jj limit offset 在 select 语法的核心处理逻辑:
-> 定义变量
-> 定义处理逻辑
-> 返回自定义 SqlNode
04
parserImpl.ftl 定义扩展的 SqlNode ExtendDollarVariable:
config.fmpp 定义包以及扩展实现类的 import:
扩展 SqlNode 实现:
-> 变量实现 sqlNode
-> 扩展 limit 实现类 ExtendSqlOrderBy,该类实现了 SqlOrderBy,并在此基础扩展了 limit 的 SqlNode,以及 isOffsetLimit、isLimitOffset、isOnlyLimit 三个 boolean 标识 limit 的不同语法
通过上面的这些步骤后,最后解析生成的 SqlNode 语法树如下所示:
扩展 SQL 在数栈的应用
目前袋鼠云的底层 sqlparser sql 解析涉及的子产品应用包括 API 数据服务、离线开发、客户数据洞察(标签)、实时开发等,虽然大部分针对 Calcite 的 SQL 语法扩展相对于上层的产品应用感知不是很明显,但是扩展 SQL 还是解决了一些痛点,主要如下:
・逐渐替换底层采用了多种解析工具解析的情况,使维护更简单,减少 bug 的产生
・解决一些不支持的语法,避免在上层业务层做处理或者在底层做一些特殊处理
以在 API 数据服务后续接入的 like 语法改造为例为大家进行分享,目前的 API 数据服务中支持 like ${var} 语法,在执行测试中通过传递 like 语法来确定执行的模糊匹配方式,例如 % xx、xx%、% xx%。
收到客户提出的优化 like 语法场景,袋鼠云本着客户第一的原则,这种合理的优化需求是采纳的。SQL 支持 like%${var}、${var}%、%${var}%,这样在执行测试中就不需要输入 % 了,目前扩展 SQL 语法已经支持这种优化的 like 语法,预计在 2023 年上半年会接入进去,下面通过 API 数据服务展示当前 like SQL 和扩展后的 SQL 差异:
● 当前 like ${var} 处理
-> 生成 API
-> 测试执行,模糊匹配需要输入 %
● 扩展 like %${var}%
-> 生成 API
-> 测试执行,由于在 SQL 阶段已经写了模糊匹配方式,因此可以直接输入值
总结规划
相信通过上面的案例后,大家对于 Calcite 扩展 SQL 语法的流程应该有了大致的了解,目前在袋鼠云的业务场景中已经扩展了许多语法,在未来还有一些工作需要进行优化:
・丰富 SQL 语法,实现不同数据源扩展 SQL 语法的隔离
・逐渐通过 SQL 语法扩展替换掉底层 Calcite 和 druid 共同解析的场景,避免维护多套相同的解析,减少线上问题产生
最后如果是初步接触 Calcite SQL 语法扩展的同学们,建议先熟悉 javacc 语法。
地址:https://javacc.github.io/javacc/
想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=szalykfz
同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术 qun」,交流最新开源技术信息,qun 号码:30537511,项目地址:https://github.com/DTStack