【StarRocks系列】 Trino 方言支持
我们在之前的文章中,介绍了 Doris 官方提供的两种方言转换工具,分别是 sql convertor 和方言 plugin。StarRocks 目前同样也提供了类似的方言转换功能。本文我们就一起来看一下这个功能的实现与 Doris 相比有何不同。
一、Trino 方言验证
我们可以通过如下 SQL 来验证 Trino 的方言转换在 SR 中的效果:
set enable_profile = true;
set sql_dialect = starrocks;
select BOROUGH, approx_count_distinct(ZIP_CODE) cnt from crashdata group by BOROUGH order by cnt desc;
set sql_dialect = trino;
select BOROUGH, approx_distinct("ZIP_CODE") cnt from crashdata group by BOROUGH order by cnt desc;
针对上述查询,我们在 SR 集群执行,结果如下所示:

可以看到,执行结果完全一致,说明方言转换已经生效,并且符合预期。通过 WebUI 查看提交的两条 SQL:

页面上显示的仍然是原始的 SQL 而不是改写之后的。如果在 SR 的方言下,执行 SQL2 的话,那么会直接报错,如下所示:

提示函数找不到,这也是符合预期的,因为 approx_distinct 这个函数在 SR 中是不存在的。下面我们就结合代码来看一下 SR 是如何实现这个方言转换功能的。
二、Trino AST 简介
由于 Trino 的方言支持,主要思路是将 Trino 的相关结构转换成 SR 的结构,因此这里先简单了解下 Trino 的 AST 相关结构:

后续提到的相关结构,就可以直接参考上述图片。
三、Trino Transformer 介绍
SR 在 FE 中实现了一套 transformer 可以将 Trino 的 function 转换为 SR 的 function,从而实现了方言转换的功能。
3.1 整体流程图
整个 parse 的相关流程如下所示:

上述流程可以分为如下几个步骤:
- FE 启动的时候,Trino2SRFunctionCallTransformer 会通过静态方法将所有 Trino 到 SR 的函数映射注册到 TRANSFORMER_MAP 这个 map 中;
- FE 调用 Trino 的 SqlParser 将 sql string 转换为 Trino 的 Statement 结构,可以参考上述的 AST 结构图;
- 在 trino/AstBuilder 中,将 Trino 的 Statement 转换为 SR 的 StatementBase,transformer 用于进行函数转换;
- 根据 sql 中的 Trino 函数名,在 Map 中进行匹配,找到对应的 FunctionCallTransformer;
- 在 FunctionCallTransformer 中构造 FunctionCallRewriter,最终返回至 trino/AstBuilder,完成函数的转换;
- 继续后续的其他操作,最终生成 SR 的 StatementBase结构,完成 parse 操作。
这里我们来一一看下对应的操作。
3.2 函数映射注册
Trino 到 SR 的函数映射注册代码位于 Trino2SRFunctionCallTransformer 中,这个类在加载的时候,会完成对应的函数映射注册,如下所示:
private static void registerAllFunctionTransformer() {registerAggregateFunctionTransformer();registerArrayFunctionTransformer();registerDateFunctionTransformer();registerStringFunctionTransformer();registerRegexpFunctionTransformer();registerJsonFunctionTransformer();registerURLFunctionTransformer();registerBitwiseFunctionTransformer();registerUnicodeFunctionTransformer();registerMapFunctionTransformer();registerBinaryFunctionTransformer();// todo: support more function transform
}
以 registerAggregateFunctionTransformer 为例,这里负责对聚合函数的映射进行注册,相关代码如下所示:
private static void registerAggregateFunctionTransformer() {// 1.approx_distinctregisterFunctionTransformer("approx_distinct", 1,"approx_count_distinct", ImmutableList.of(Expr.class));// 2. arbitraryregisterFunctionTransformer("arbitrary", 1,"any_value", ImmutableList.of(Expr.class));// 3. approx_percentileregisterFunctionTransformer("approx_percentile", 2,"percentile_approx", ImmutableList.of(Expr.class, Expr.class));
通过这个函数的映射,Trino 的 approx_distinct 函数就会被转换成 SR 的 approx_count_distinct,对应的结构体转换如下所示:

可以看到,最终在 map 中保存了 approx_distinct 这个 Trino 函数到 SR 的映射。需要注意的是,map 的 value 是一个 list,主要是为了处理参数不同的重载函数。通过 debug 可以直接查看已经注册的函数映射:

其中,PlaceholderExpr 就是用来保存 SR 函数的输入参数,主要就是 index 和 参数的类型,后续用于进行匹配,最终会被替换成实际的函数参数,位于 FunctionCallRewriter 的 sourceArguments 中。
3.3 Transformer 匹配
在 FE 启动之后,Trino 的函数映射已经全部注册完成。当我们通过设置方言为 trino 之后,首先需要根据 Trino 的函数名去 map 中进行匹配,由于重载函数的存在,因此还需要比较对应的参数类型。相关的函数调用如下所示:
parse(SqlParser.java):56
--parseWithTrinoDialect(SqlParser.java):68/74
---toStatement(TrinoParserUtils.java):42
----accept(io/trino/sql/tree/Statement.java)
// 省略部分函数调用栈
-visitFunctionCall(trino/AstBuilder.java):713
--convert(Trino2SRFunctionCallTransformer.java):47
---convertRegisterFn(Trino2SRFunctionCallTransformer.java):62
----match(FunctionCallTransformer.java)
其中,match函数的参数为 List<Expr>,对应的就是 2.1 中 List<Expression> 转换后的结果,即将 Trino 的 Expression 转换为 SR 中的 Expr 结构。匹配的过程主要分为两步:
- 比较参数个数是否一致;
- 比较每一个 PlaceholderExpr 的类型,是否是实际参数类型的超类。
两个条件都满足的话,则证明这个 transformer 是相符的,则继续进行后续的转换。
3.4 函数转换
转换操作主要就是生成一个 FunctionCallRewriter 对象,相关的函数调用如下所示:
visitFunctionCall(trino/AstBuilder.java):713
-convert(Trino2SRFunctionCallTransformer.java):47
--convertRegisterFn(Trino2SRFunctionCallTransformer.java):69
---transform(FunctionCallTransformer.java):113
----ctor(FunctionCallRewriter.java)
我们通过 debug 分别对比下 Trino 和 SR 中的function call 的结构,如下所示:


可以看到,最终生成的 FunctionCallRewriter 中,已经包含了具体的参数,即 ZIP_CODE 这个列,对应的类型是 SlotRef,而 2.2 中的 PlaceholderExpr 只有类型信息,即 Expr(SlotRef是其一个子类,所以类型可以匹配上)。
四、总结
4.1 与 Doris 方言功能比较
由于 Doris 的 sql convertor 工具是借助 SqlGlot 实现的,因此与 Doris 本身关系不大。这里我们主要比较下 Doris 的方言 plugin 与 SR 的 transformer 优缺点:
| 方言功能 | 优点 | 缺点 |
|---|---|---|
| SR Transformer | 实现比较完善,支持各种函数转换 | 与SR代码耦合紧,并且仅支持Trino,扩展性一般 |
| Doris Plugin | 通过Plugin的方法与Doris代码进行了解耦,扩展性相对较好,目前支持Trino和Spark | 实现比较简单,函数转换未提供,并且存在一些问题,可用性较差 |
4.2 思考小结
由于目前 SR 官方只支持 Trino 的方言转换,并且与源码耦合比较紧,如下所示:
// SqlParser.java
public static List<StatementBase> parse(String sql, SessionVariable sessionVariable) {if (sessionVariable.getSqlDialect().equalsIgnoreCase("trino")) {return parseWithTrinoDialect(sql, sessionVariable);} else {return parseWithStarRocksDialect(sql, sessionVariable);}
}
如果想要完整的支持一种新的方言转换,需要实现对应的 FunctionCallTransformer 和 AstBuilder,并修改上述的 if-else,总体代码开发量比较大。此外,本文的所有内容是笔者基于 StarRocks-3.3 版本分析、总结而来,如有错误,欢迎指正。
五、参考文档
- sql_dialect;
- [Feature] Support Trino parser on StarRocks #14830;
相关文章:
【StarRocks系列】 Trino 方言支持
我们在之前的文章中,介绍了 Doris 官方提供的两种方言转换工具,分别是 sql convertor 和方言 plugin。StarRocks 目前同样也提供了类似的方言转换功能。本文我们就一起来看一下这个功能的实现与 Doris 相比有何不同。 一、Trino 方言验证 我们可以通过…...
【2024最新华为OD-C卷试题汇总】URL拼接 (100分) - 三语言AC题解(Python/Java/Cpp)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 文章目录 前…...
【ARM 嵌入式 C 字符串系列 23.7 -- C 实现函数 isdigit 和 isxdigit】
请阅读【嵌入式开发学习必备专栏 】 文章目录 isdigit 和 isxdigit C代码实现实现 isdigit实现 isxdigit使用示例 isdigit 和 isxdigit C代码实现 在C语言中,isdigit和isxdigit函数用于检查一个字符是否分别为十进制数字或十六进制数字。以下是这两个函数的简单实现…...
三分钟了解计算机网络核心概念-数据链路层和物理层
计算机网络数据链路层和物理层 节点:一般指链路层协议中的设备。 链路:一般把沿着通信路径连接相邻节点的通信信道称为链路。 MAC 协议:媒体访问控制协议,它规定了帧在链路上传输的规则。 奇偶校验位:一种差错检测方…...
数据结构===堆
文章目录 概要堆2条件大顶堆小顶堆 堆的实现插入元素删除堆顶元素 堆代码小结 概要 堆,有趣的数据结构。 那么,如何实现一个堆呢? 堆 堆,有哪些重点: 满足2条件大顶堆小顶堆 2条件 2条件: 堆是一个…...
AAA、RADIUS、TACACS、Diameter协议介绍
准备软考高级时碰到的一个概念,于是搜集网络资源整理得出此文。 概述 AAA是Authentication、Authorization、Accounting的缩写简称,即认证、授权、记帐。Cisco开发的一个提供网络安全的系统。AAA协议决定哪些用户能够访问服务,以及用户能够…...
Nacos高频面试题及参考答案(2万字长文)
目录 Nacos是什么?它的主要功能有哪些? Nacos在微服务架构中扮演什么角色?...
CMakeLists.txt语法规则:条件判断中表达式说明四
一. 简介 前面学习了 CMakeLists.txt语法中的 部分常用命令,常量变量,双引号的使用。 前面几篇文章也简单了解了 CMakeLists.txt语法中的条件判断,文章如下: CMakeLists.txt语法规则:条件判断说明一-CSDN博客 CMa…...
Hive概述
Hive简介 Hive是一个基于Hadoop的开源数据仓库工具,用于存储和处理海量结构化数据. 它是Facebook在2008年8月开源的一个数据仓库框架,提供了类似于SQL语法的HQL(HiveSQL)语句作为数据访问接口. Hive可以做复查统计分析之类的工作; 利用hdfs的存储空间,进行结构化数据的存储; 利…...
buuctf-misc-33.[BJDCTF2020]藏藏藏1
33.[BJDCTF2020]藏藏藏1 题目:藏了很多层,一层一层的剥开 常规思路,先使用010打开一下看看 binwalk不行用foremost 发现是pk文件也就是压缩包,并且包含了docx文件 这不binwalk分离一下文件?虽然可以看出有隐藏文件&…...
golang 基础知识细节回顾
之前学习golang的速度过于快,部分内容有点囫囵吞枣的感觉,写gorm过程中有很多违反我常识的地方,我通过复习去修正了我之前认知错误和遗漏的地方。 itoa itoa自增的作用在编辑error code时候作用很大,之前编辑springboot的error c…...
递归陷阱七例
目录 栈溢出 无限递归 大常数参数 递归深度过大 重复计算 函数调用开销 递归与迭代的选择 总结 递归是一种强大的编程技术,它允许函数调用自身。递归在很多情况下可以简化代码,使问题更容易理解和解决。然而,递归也容易导致一些常见的…...
【3D基础】坐标转换——地理坐标投影到平面
汤国安版GIS原理第二章重点 1.常见投影方式 https://download.csdn.net/blog/column/9283203/83387473 Web Mercator投影(Web Mercator Projection): 优点: 在 Web 地图中广泛使用,易于显示并与在线地图服务集成。在…...
颈椎锻炼方式
1. 颈部伸展运动:坐直,慢慢将头向前伸展,直到感到轻微的拉伸,保持数秒钟,然后缓慢放松。重复10次。 2. 颈部旋转运动:坐直,慢慢将头向一侧转动,直到感到轻微的拉伸,保持…...
测试环境搭建:JDK+Tomcat+Mysql+Redis
基础的测试环境搭建: LAMPLinux(CentOS、ubuntu、redhat)ApacheMysqlPHP LTMJLinux(CentOS、ubuntu、redhat)TomcatMysql(Oracle)RedisJava 真实的测试环境搭建:(企业真实的运维) 基于SpringBoot(SpringCloud分布式微…...
(delphi11最新学习资料) Object Pascal 学习笔记---第11章第1节(混合引用中的错误)
11.1.3 混合引用中的错误 在使用对象时,你通常应该只使用对象变量或接口变量来访问它们。混合使用这两种方法会破坏对象 Pascal 所提供的引用计数机制,并可能导致极难跟踪的内存错误。在实践中,如果你决定使用接口,你可能应该…...
代码随想录算法训练营第三天 | 链表理论基础,203.移除链表元素,707.设计链表,206.反转链表
对于链表完全陌生,但是看题目又觉得和数组一样的 链表理论基础 Q:什么是链表? A:链表是由一系列结点组成的。每一个结点由两部分组成:数据和指针。 203.移除链表元素 题目: 给你一个链表的头节点 head 和…...
如何利用仪表构造InfiniBand流量在数据中心测试中的应用
一、什么是Infiniband? 在当今数据爆炸的时代,数据中心作为信息处理的中心枢纽,面临着前所未有的挑战。传统的通信方式已经难以满足日益增长的数据传输需求,而InfiniBand技术的出现,为数据中心带来了全新的通信解决方…...
Kubernetes 文档 / 概念 / Kubernetes 架构 / 节点
Kubernetes 文档 / 概念 / Kubernetes 架构 / 节点 此文档从 Kubernetes 官网摘录 中文地址 英文地址 节点上的组件包括 kubelet、 容器运行时以及 kube-proxy。 管理 向 API 服务器添加节点的方式主要有两种: 节点上的 kubelet 向控制面执行自注册;…...
ICode国际青少年编程竞赛- Python-1级训练场-for循环练习
ICode国际青少年编程竞赛- Python-1级训练场-for循环练习 1、 for i in range(3):Dev.step(4)Dev.turnLeft()2、 for i in range(3):Dev.step(2)Dev.turnRight()Dev.step(2)Dev.turnLeft()3、 for i in range(3):Dev.step(2)Dev.turnRight()Dev.step(2)Dev.turnLeft()4、 for…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
