看这篇就够了丨基于Calcite框架的SQL语法扩展探索

本文涉及的产品
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Calcite在大数据系统中有着广泛的运用, 比如Apache Flink, Apache Drill等都大量使用了Calcite,理解Calcite的原理可以说已经成为理解大数据系统中SQL访问层实现原理的必备条件之一。本文就为大家详细介绍如何基于Calcite框架的SQL语法扩展探索使之更符合你的业务需求,以及扩展SQL在数栈产品的应用实践。对该技术感兴趣的同学点进文章阅读哦

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:

网络异常,图片无法展示
|
02

Parser.jj 扩展的变量方法接入,下面方法会在解析到 limit、offset 关键字后面的一个词时进行调用:

网络异常,图片无法展示
|
03

Parser.jj limit offset 在 select 语法的核心处理逻辑:

-> 定义变量

网络异常,图片无法展示
|
主要定义了三个 boolean 类型的变量,isOffsetLimit 表示 offset limit 语法,isLimitOffset 表示 limit offset 语法,isOnlyLimit 表示 limit count、limit start count 语法。

-> 定义处理逻辑

网络异常,图片无法展示
|

-> 返回自定义 SqlNode

网络异常,图片无法展示
|
针对符合上面的三个 boolean 条件时,使用自定义 ExtendSqlOrderBy 的扩展类。

04

parserImpl.ftl 定义扩展的 SqlNode ExtendDollarVariable:

网络异常,图片无法展示
|
05

config.fmpp 定义包以及扩展实现类的 import:

网络异常,图片无法展示
|
06

扩展 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

相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps 
目录
相关文章
|
1月前
|
SQL 数据采集 自然语言处理
NL2SQL之DB-GPT-Hub<详解篇>:text2sql任务的微调框架和基准对比
NL2SQL之DB-GPT-Hub<详解篇>:text2sql任务的微调框架和基准对比
|
3月前
|
SQL Java 数据库
建模底层逻辑问题之ORM框架建模中,执行SQL的过程中被抽象和组织是如何实现的
建模底层逻辑问题之ORM框架建模中,执行SQL的过程中被抽象和组织是如何实现的
|
5天前
|
SQL 开发框架 .NET
突破T-SQL限制:利用CLR集成扩展RDS SQL Server的功能边界
CLR集成为SQL Server提供了强大的扩展能力,突破了T-SQL的限制,极大地拓展了SQL 的应用场景,如:复杂字符串处理、高性能计算、图像处理、机器学习集成、自定义加密解密等,使开发人员能够利用 .NET Framework的丰富功能来处理复杂的数据库任务。
|
15天前
|
SQL 存储 关系型数据库
SQL `CREATE DATABASE` 语法
【11月更文挑战第10天】
39 3
|
21天前
|
SQL 关系型数据库 数据库
sql语法
【10月更文挑战第26天】sql语法
31 5
|
28天前
|
SQL 数据库
SQL数据库基础语法入门
[link](http://www.vvo.net.cn/post/082935.html)
|
1月前
|
SQL 存储 关系型数据库
mysql SQL必知语法
本文详细介绍了MySQLSQL的基本语法,包括SELECT、FROM、WHERE、GROUPBY、HAVING、ORDERBY等关键字的使用,以及数据库操作如创建、删除表,数据类型,插入、查询、过滤、排序、连接和汇总数据的方法。通过学习这些内容,读者将能更好地管理和操
17 0
|
1月前
|
SQL 关系型数据库 MySQL
Mysql(2)—SQL语法详解
SQL(Structured Query Language,结构化查询语言)是一种用于管理关系型数据库的标准编程语言。它主要用于数据的查询、插入、更新和删除等操作。SQL最初在1970年代由IBM的研究人员开发,旨在处理关系数据模型。
29 0
|
2月前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)
|
3月前
|
SQL 关系型数据库 MySQL
INSERT INTO t_a.tableName SELECT * FROM t_b.tableName 如何通过定义一个list对象,包含多个tableName,循环执行前面的sql,用MySQL的语法写
【8月更文挑战第7天】INSERT INTO t_a.tableName SELECT * FROM t_b.tableName 如何通过定义一个list对象,包含多个tableName,循环执行前面的sql,用MySQL的语法写
42 5