十分钟成为 TiDB Contributor | 添加內建函数
在数据库实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天golang学习网就整理分享《十分钟成为 TiDB Contributor | 添加內建函数》,聊聊MySQL、github、NoSQL、rust,希望可以帮助到正在努力赚钱的你。
最近我们对 TiDB 代码做了些改进,大幅度简化了添加內建函数的流程,这篇教程为大家分享如何为 TiDB 新增 builtin 函数。首先介绍一些必需的背景知识,然后介绍增加 builtin 函数的流程,最后会以一个函数作为示例。
背景知识
SQL 语句发送到 TiDB 后首先会经过 parser,从文本 parse 成为 AST(抽象语法树),通过 Query Optimizer 生成执行计划,得到一个可以执行的 plan,通过执行这个 plan 即可得到结果,这期间会涉及到如何获取 table 中的数据,如何对数据进行过滤、计算、排序、聚合、滤重以及如何对表达式进行求值。
对于一个 builtin 函数,比较重要的是进行语法解析以及如何求值。其中语法解析部分需要了解如何写 yacc 以及如何修改 TiDB 的词法解析器,较为繁琐,我们已经将这部分工作提前做好,大多数 builtin 函数的语法解析工作已经做完。
对 builtin 函数的求值需要在 TiDB 的表达式求值框架下完成,每个 builtin 函数被认为是一个表达式,用一个 ScalarFunction 来表示,每个 builtin 函数通过其函数名以及参数,获取对应的函数类型以及函数签名,然后通过函数签名进行求值。
总体而言,上述流程对于不熟悉 TiDB 的朋友而言比较复杂,我们对这部分做了些工作,将一些流程性、较为繁琐的工作做了统一处理,目前已经将大多数未实现的 buitlin 函数的语法解析以及寻找函数签名的工作完成,但是函数实现部分留空。换句话说,只要找到留空的函数实现,将其补充完整,即可作为一个 PR。
添加 builtin 函数整体流程
找到未实现的函数
在 TiDB 源码中的 expression 目录下搜索errFunctionNotExists
,即可找到所有未实现的函数,从中选择一个感兴趣的函数,比如 SHA2 函数:
func (b *builtinSHA2Sig) eval(row []types.Datum) (d types.Datum, err error) {
return d, errFunctionNotExists.GenByArgs("SHA2")
}
实现函数签名
接下来要做的事情就是实现 eval 方法,函数的功能请参考 MySQL 文档,具体的实现方法可以参考目前已经实现函数。在 typeinferer 中添加类型推导信息
在 plan/typeinferer.go 中的 handleFuncCallExpr() 里面添加这个函数的返回结果类型,请保持和 MySQL 的结果一致。全部类型定义参见 MySQL Const。
* 注意大多数函数除了需要填写返回值类型之外,还需要获取返回值的长度。
写单元测试
在 expression 目录下,为函数的实现增加单元测试,同时也要在 plan/typeinferer_test.go 文件中添加 typeinferer 的单元测试运行 make dev,确保所有的 test case 都能跑过
示例
这里以新增 SHA1() 函数的 PR 为例,进行详细说明
首先看
expression/builtin_encryption.go:
将 SHA1() 的求值方法补充完整
func (b *builtinSHA1Sig) eval(row []types.Datum) (d types.Datum, err error) {
// 首先对参数进行求值,这块一般不用修改
args, err := b.evalArgs(row)
if err != nil {
return types.Datum{}, errors.Trace(err)
}
// 每个参数的意义请参考 MySQL 文档
// SHA/SHA1 function only accept 1 parameter
arg := args[0]
if arg.IsNull() {
return d, nil
}
// 这里对参数值做了一个类型转换,函数的实现请参考 util/types/datum.go
bin, err := arg.ToBytes()
if err != nil {
return d, errors.Trace(err)
}
hasher := sha1.New()
hasher.Write(bin)
data := fmt.Sprintf("%x", hasher.Sum(nil))
// 设置返回值
d.SetString(data)
return d, nil
}
接下来给函数实现添加单元测试,参见
expression/builtin_encryption_test.go:
var shaCases = []struct {
origin interface{}
crypt string
}{
{"test", "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"},
{"c4pt0r", "034923dcabf099fc4c8917c0ab91ffcd4c2578a6"},
{"pingcap", "73bf9ef43a44f42e2ea2894d62f0917af149a006"},
{"foobar", "8843d7f92416211de9ebb963ff4ce28125932878"},
{1024, "128351137a9c47206c4507dcf2e6fbeeca3a9079"},
{123.45, "22f8b438ad7e89300b51d88684f3f0b9fa1d7a32"},
}
func (s *testEvaluatorSuite) TestShaEncrypt(c *C) {
defer testleak.AfterTest(c)() // 监测 goroutine 泄漏的工具,可以直接照搬
fc := funcs[ast.SHA]
for _, test := range shaCases {
in := types.NewDatum(test.origin)
f, _ := fc.getFunction(datumsToConstants([]types.Datum{in}), s.ctx)
crypt, err := f.eval(nil)
c.Assert(err, IsNil)
res, err := crypt.ToString()
c.Assert(err, IsNil)
c.Assert(res, Equals, test.crypt)
}
// test NULL input for sha
var argNull types.Datum
f, _ := fc.getFunction(datumsToConstants([]types.Datum{argNull}), s.ctx)
crypt, err := f.eval(nil)
c.Assert(err, IsNil)
c.Assert(crypt.IsNull(), IsTrue)
}
* 注意,除了正常 case 之外,最好能添加一些异常的case,如输入值为 nil,或者是多种类型的参数
最后还需要添加类型推导信息以及 test case,参见
plan/typeinferer.go,
plan/typeinferer_test.go:
case ast.SHA, ast.SHA1:
tp = types.NewFieldType(mysql.TypeVarString)
chs = v.defaultCharset
tp.Flen = 40
{`sha1(123)`, mysql.TypeVarString, "utf8"},
{`sha(123)`, mysql.TypeVarString, "utf8"},
编辑按:添加 TiDB Robot 微信,加入 TiDB Contributor Club,无门槛参与开源项目,改变世界从这里开始吧(萌萌哒)。

听说现在成为 TiDB Contributor 就送限量版马克杯呦~

好了,本文到此结束,带大家了解了《十分钟成为 TiDB Contributor | 添加內建函数》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多数据库知识!
PHP+Mysql高并发解决
- 上一篇
- PHP+Mysql高并发解决
- 下一篇
- READ-COMMITED 与 REPEATABLE-READ 事务隔离级别之间的异同
-
- 坚定的美女
- 真优秀,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢作者分享技术贴!
- 2023-03-10 16:07:51
-
- 明亮的康乃馨
- 很详细,已收藏,感谢作者的这篇文章内容,我会继续支持!
- 2023-03-09 08:43:34
-
- 苹果发箍
- 这篇文章出现的刚刚好,好细啊,受益颇多,码起来,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-03-07 06:17:54
-
- 数据库 · MySQL | 2小时前 |
- 部分更新 怎样减少 JSON 写放大
- 231浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- EXPLAIN FORMAT=JSON 怎样读取窗口节点
- 311浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- JSON 路径索引 怎样减少文档扫描
- 416浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- ONLY_FULL_GROUP_BY 怎样处理函数依赖
- 365浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- 不可见索引 怎样做安全回滚试验
- 254浏览 收藏
-
- 数据库 · MySQL | 2小时前 | MySQL · mysql optimizer_switch 派生表 条件下推
- optimizer_switch 怎样控制派生条件下推
- 358浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- GTID 怎样查看组复制事务标识
- 228浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- GeoJSON 怎样提取空间属性
- 462浏览 收藏
-
- 数据库 · MySQL | 2小时前 |
- 窗口函数 怎样读取 windowing 计划
- 130浏览 收藏
-
- 数据库 · MySQL | 2小时前 | MySQL · JSON · 数据校验 · JSON_SCHEMA_VALID CHECK 约束 MySQL JSON Schema JSON 结构校验 JSON_SCHEMA_VALIDATION_REPORT
- JSON_SCHEMA_VALID 怎样先判断结构
- 293浏览 收藏
-
- 数据库 · MySQL | 18小时前 |
- MySQL histogram 怎样用直方图解释选择性估算
- 122浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 684次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 699次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 751次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 716次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 552次使用
-
- MySQL 明明加了索引,为什么查询还是很慢?先查这 6 个点
- 2026-06-27 374浏览
-
- golang MySQL实现对数据库表存储获取操作示例
- 2022-12-22 499浏览
-
- golang 基于 mysql 简单实现分布式读写锁
- 2023-01-07 384浏览
-
- 详解如何利用GORM实现MySQL事务
- 2023-01-07 184浏览
-
- Go语言实现操作MySQL的基础知识总结
- 2023-01-23 265浏览

