十分钟成为 Contributor 系列:重构内建函数进度报告

author: 徐怀宇

6 月 22 日,TiDB 发布了一篇如何十分钟成为 TiDB Contributor 系列的第二篇文章,向大家介绍如何为 TiDB 重构 built-in 函数。

截止到目前,得到了来自社区的积极支持与热情反馈,TiDB 参考社区 contributors 的建议,对计算框架进行了部分修改以降低社区同学参与的难度。

本文完成以下2 项工作,希望帮助社区更好的参与进 TiDB 的项目中来:

  1. 对尚未重写的 built-in 函数进行陈列
  2. 对继上篇文章后,计算框架所进行的修改,进行详细介绍

一. 尚未重写的 built-in 函数陈列如下:

共计 165 个 在 expression 目录下运行 grep -rn "^\tbaseBuiltinFunc$" -B 1 * | grep "Sig struct {" | awk -F "Sig" '{print $1}' | awk -F "builtin" '{print $3}' > ~/Desktop/func.txt 命令可以获得所有未实现的 built-in 函数

0

1

2

3

4

Coalesce

Uncompress

Log10

Default

UnaryOp

Greatest

UncompressedLength

Rand

InetAton

IsNull

Least

ValidatePasswordStrength

Pow

InetNtoa

In

Interval

Database

Round

Inet6Aton

Row

CaseWhen

FoundRows

Conv

Inet6Ntoa

SetVar

If

CurrentUser

CRC32

IsFreeLock

GetVar

IfNull

User

Sqrt

IsIPv4

Values

NullIf

ConnectionID

Arithmetic

IsIPv4Prefixed

BitCount

AesDecrypt

LastInsertID

Acos

IsIPv6

Reverse

AesEncrypt

Version

Asin

IsUsedLock

Convert

Compress

Benchmark

Atan

MasterPosWait

Substring

Decode

Charset

Cot

NameConst

SubstringIndex

DesDecrypt

Coercibility

Exp

ReleaseAllLocks

Locate

DesEncrypt

Collation

PI

UUID

Hex

Encode

RowCount

Radians

UUIDShort

UnHex

Encrypt

Regexp

Truncate

AndAnd

Trim

OldPassword

Abs

Sleep

OrOr

LTrim

RandomBytes

Ceil

Lock

LogicXor

RTrim

SHA1

Floor

ReleaseLock

BitOp

Rpad

SHA2

Log

AnyValue

IsTrueOp

BitLength

Char

Format

FromDays

DayOfWeek

Timestamp

CharLength

FromBase64

Hour

DayOfYear

AddTime

FindInSet

InsertFunc

Minute

Week

ConvertTz

Field

Instr

Second

WeekDay

MakeTime

MakeSet

LoadFile

MicroSecond

WeekOfYear

PeriodAdd

Oct

Lpad

Month

Year

PeriodDiff

Quote

Date

MonthName

YearWeek

Quarter

Bin

DateDiff

Now

FromUnixTime

SecToTime

Elt

TimeDiff

DayName

GetFormat

SubTime

ExportSet

DateFormat

DayOfMonth

StrToDate

TimeFormat

UTCTim

ToSeconds

TimestampDiff

DateArith

Extract

UnixTimestamp

UTCTimestamp

UTCDate

Time

CurrentTime

ToDays

TimestampAdd

TimeToSec

CurrentDate

SysDate

二. 计算框架进行的修改:

此处依然使用 Length 函数( expression/builtin_string.go )为例进行说明,与前文采取相同目录结构:

1. expression/builtin_string.go

(1)lengthFunctionClass.getFunction() 方法: 简化类型推导实现

getFunction 方法用来生成 built-in 函数对应的函数签名,在构造 ScalarFunction 时被调用

func (c *lengthFunctionClass) getFunction(args []Expression, ctx context.Context) (builtinFunc, error) {
    // 此处简化类型推导过程,对 newBaseBuiltinFuncWithTp() 实现进行修改,新的实现中,传入 Length 返回值类型 tpInt 表示返回值类型为 int,参数类型 tpString 表示返回值类型为 string 
  bf, err := newBaseBuiltinFuncWithTp(args, ctx, tpInt, tpString)
  if err != nil {
    return nil, errors.Trace(err)
  }
  // 此处参考 MySQL 实现,设置返回值长度为 10(character length)
  // 对于 int/double/decimal/time/duration 类型返回值,已在 newBaseBuiltinFuncWithTp() 中默认调用 types.setBinChsClnFlag() 方法,此处无需再进行设置
  bf.tp.Flen = 10
  sig := &builtinLengthSig{baseIntBuiltinFunc{bf}}
  return sig.setSelf(sig), errors.Trace(c.verifyArgs(args))
}

注:

  • 对于返回值类型为 string 的函数,需要,注意参考 MySQL 行为设置 bf.tp.[charset | collate | flag] 查看 MySQL 行为可以通过在终端启动 $ mysql -uroot \-\-column-type-info,这样对于每一个查询语句,可以查看每一列详细的 metadata 对于返回值类型为 string 的函数,以 concat 为例,当存在类型为 string 且包含 binary flag 的参数时,其返回值也应设置 binary flag
  • 对于返回值类型为 Time 的函数,需要注意,根据函数行为,设置 bf.tp.Tp = [ TypeDate | TypeDatetime | TypeTimestamp ] , 若为 TypeDate/ TypeDatetime,还需注意推导 bf.tp.Decimal (即小数位数)
  • 不确定性的函数:

0

1

2

3

4

5

Rand

ConnectionID

CurrentUser

User

Database

RowCount

Schema

FoundRows

LastInsertId

Version

Sleep

UUID

GetVar

SetVar

Values

SessionUser

SystemUser

(2)实现 builtinLengthSig.evalInt() 方法:保持不变,此处请注意修改该函数的注释 (s/ eval/ evalXXX)

2. expression/builtin_string_test.go

func (s *testEvaluatorSuite) TestLength(c *C) {
   defer testleak.AfterTest(c)()
   cases := []struct {
      args     interface{}
      expected int64
      isNil    bool
      getErr   bool
   }{
     ......
   }

   for _, t := range cases {
      f, err := newFunctionForTest(s.ctx, ast.Length, primitiveValsToConstants([]interface{}{t.args})...)
      c.Assert(err, IsNil)
      d, err := f.Eval(nil)
     // 注意此处不再对 LENGTH 函数的返回值类型进行测试,相应测试被移动到 plan/typeinfer_test.go/TestInferType 函数中,(注意不是expression/typeinferer_test.go)
      if t.getErr {
         c.Assert(err, NotNil)
      } else {
         c.Assert(err, IsNil)
         if t.isNil {
            c.Assert(d.Kind(), Equals, types.KindNull)
         } else {
            c.Assert(d.GetInt64(), Equals, t.expected)
         }
      }
   }

   // 测试函数是否具有确定性
   // 在 review 社区的 PRs 过程中发现,这个测试经常会被遗漏,烦请留意
   f, err := funcs[ast.Length].getFunction([]Expression{Zero}, s.ctx)
   c.Assert(err, IsNil)
   c.Assert(f.isDeterministic(), IsTrue)
}

3. executor/executor_test.go

与上一篇文章保持不变,需要注意的是,为了保证可读性, TestStringBuiltin()方法仅对 expression/builtin_string.go 文件中的 built-in 函数进行测试。如果 executor_test.go 文件中不存在对应的 TestXXXBuiltin() 方法,可以新建一个对应的测试函数。

4. plan/typeinfer_test.go

func (s *testPlanSuite) TestInferType(c *C) {
  ....
   tests := []struct {
      sql     string
      tp      byte
      chs     string
      flag    byte
      flen    int
      decimal int
   }{
     ...
     // 此处添加对 length 函数返回值类型的测试
     // 此处注意,对于返回值类型、长度等受参数影响的函数,此处测试请尽量覆盖全面
      {"length(c_char, c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 10, 0},
     ...
   }
   for _, tt := range tests {
      ...
   }
}

注:

当有多个 PR 同时在该文件中添加测试时,若有别的 contributor 的 PR 先于自己的 PR merge 进 master,有可能会发生冲突,此时在本地 merge 一下 master 分支,解决一下再 push 一下即可。

成为 New Contributor 赠送限量版马克杯的活动还在继续中,任何一个新加入集体的小伙伴都将收到我们充满了诚意的礼物,很荣幸能够认识你,也很高兴能和你一起坚定地走得更远。

成为 New Contributor 获赠限量版马克杯,马克杯获取流程如下:

  1. 提交 PR
  2. PR提交之后,请耐心等待维护者进行 Review。 目前一般在一到两个工作日内都会进行 Review,如果当前的 PR 堆积数量较多可能回复会比较慢。 代码提交后 CI 会执行我们内部的测试,你需要保证所有的单元测试是可以通过的。期间可能有其它的提交会与当前 PR 冲突,这时需要修复冲突。 维护者在 Review 过程中可能会提出一些修改意见。修改完成之后如果 reviewer 认为没问题了,你会收到 LGTM(looks good to me) 的回复。当收到两个及以上的 LGTM 后,该 PR 将会被合并。
  3. 合并 PR 后自动成为 Contributor,会收到来自 PingCAP Team 的感谢邮件,请查收邮件并填写领取表单
  4. 后台 AI 核查 GitHub ID 及资料信息,确认无误后随即便快递寄出属于你的限量版马克杯
  5. 期待你分享自己参与开源项目的感想和经验,TiDB Contributor Club 将和你一起分享开源的力量

了解更多关于 TiDB 的资料请登陆我们的官方网站:https://pingcap.com

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木宛城主

两天完成一个小型工程报价系统(三层架构)

花了两天,整理了一下三层架构,顺便练了一个小型三层架构——工程报价系统。 功能很简单,完成基本的增删改查 搭建项目三层结构 ? 界面的设计以及美化 ...

2269
来自专栏向治洪

Rxjava + retrofit + dagger2 + mvp搭建Android框架

最近出去面试,总会被问到我们项目现在采用的什么开发框架,不过据我的经验网络框架(volley)+图片缓存(uIl)+数据库(orm)+mvp,不过现在这套框架比...

19110
来自专栏cloudskyme

jbpm5.1介绍(7)

Junit测试评估流程 评估流程的界面如下: ? 这个示例里边用到了Script Task,Service Task和User Task Log执行记录日志的...

38411
来自专栏芋道源码1024

Dubbo源码解析 —— 服务引用原理

前言 经过上一篇dubbo源码解析-简单原理、与spring融合的铺垫,我们已经能简单的实现了dubbo的服务引用.其实上一篇中的代码,很多都是从dubbo源码...

2798
来自专栏草根专栏

使用两种方法让 ASP.NET Core 实现遵循 HATEOAS 结构的 RESTful API

HATEOAS(Hypermedia as the engine of application state)是 REST 架构风格中最复杂的约束,也是构建成熟 ...

49711
来自专栏菩提树下的杨过

Linq to Sql中Single写法不当可能引起的数据库查询性能低下

场景:需要从T_User表中返回指字条件的某条记录的某一个字段 在Linq中有二种理论上都行得通的写法,见下面的代码: Code using (dbUserDa...

1956
来自专栏Android知识点总结

3-AIII--Service绑定服务基于接口调用方法

1035
来自专栏陈满iOS

[iOS学习笔记]·FMDB:第三方数据库处理框架·用法示例篇(附源码Demo)

FMDatabase的实例对象_db在执行SQL语句的时候采取类似下面的代码。其中,有两种风格,一种在executeUpdate方法后面直接写上字符串的字面量语...

672
来自专栏Java3y

移动商城第三篇(商品管理)【查询商品、添加商品】

逆向生成item 将表名设置成eb_item就行了。 <table schema="" tableName="eb_item" enableCo...

3768
来自专栏草根专栏

从头编写 asp.net core 2.0 web api 基础框架 (5) EF CRUD

Github源码地址:https://github.com/solenovex/Building-asp.net-core-2-web-api-starter-...

3736

扫码关注云+社区