十分钟成为 TiDB Contributor | 添加內建函数
来源:SegmentFault
时间:2023-02-24 21:55:54 208浏览 收藏
在数据库实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天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学习网公众号,给大家分享更多数据库知识!
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
418 收藏
-
339 收藏
-
279 收藏
-
189 收藏
-
208 收藏
-
174 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习