十分钟成为 Contributor 系列 | 重构内建函数进度报告
2017-07-17 14:42
465 查看
6 月 22 日,TiDB 发布了一篇如何十分钟成为 TiDB Contributor 系列的第二篇文章,向大家介绍如何为
TiDB 重构 built-in 函数。截止到目前,得到了来自社区的积极支持与热情反馈,TiDB 参考社区 contributors 的建议,对计算框架进行了部分修改以降低社区同学参与的难度。
本文完成以下2
项工作,希望帮助社区更好的参与进 TiDB 的项目中来:
对尚未重写的 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 函数
Coalesce
Greatest
Least
Interval
CaseWhen
If
IfNull
NullIf
AesDecrypt
AesEncrypt
Compress
Decode
DesDecrypt
DesEncrypt
Encode
Encrypt
OldPassword
RandomBytes
SHA1
SHA2
Uncompress
UncompressedLength
ValidatePasswordStrength
Database
FoundRows
CurrentUser
User
ConnectionID
LastInsertID
Version
Benchmark
Charset
Coercibility
Collation
RowCount
Regexp
Abs
Ceil
Floor
Log
Log10
Rand
Pow
Round
Conv
CRC32
Sqrt
Arithmetic
Acos
Asin
Atan
Cot
Exp
PI
Radians
Truncate
Sleep
Lock
ReleaseLock
AnyValue
Default
InetAton
InetNtoa
Inet6Aton
Inet6Ntoa
IsFreeLock
IsIPv4
IsIPv4Prefixed
IsIPv6
IsUsedLock
MasterPosWait
NameConst
ReleaseAllLocks
UUID
UUIDShort
AndAnd
OrOr
LogicXor
BitOp
IsTrueOp
UnaryOp
IsNull
In
Row
SetVar
GetVar
Values
BitCount
Reverse
Convert
Substring
SubstringIndex
Locate
Hex
UnHex
Trim
LTrim
RTrim
Rpad
BitLength
Char
CharLength
FindInSet
Field
MakeSet
Oct
Quote
Bin
Elt
ExportSet
Format
FromBase64
InsertFunc
Instr
LoadFile
Lpad
Date
DateDiff
TimeDiff
DateFormat
FromDays
Hour
Minute
Second
MicroSecond
Month
MonthName
Now
DayName
DayOfMonth
DayOfWeek
DayOfYear
Week
WeekDay
WeekOfYear
Year
YearWeek
FromUnixTime
GetFormat
StrToDate
SysDate
CurrentDate
CurrentTime
Time
UTCDate
UTCTimestamp
Extract
DateArith
TimestampDiff
UnixTimestamp
Timestamp
AddTime
ConvertTz
MakeTime
PeriodAdd
PeriodDiff
Quarter
SecToTime
SubTime
TimeFormat
TimeToSec
TimestampAdd
ToDays
ToSeconds
UTCTim
此处依然使用 Length 函数( 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{
returnnil,
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))
}
注:
a. 对于返回值类型为
string 的函数,需要,注意参考 MySQL 行为设置 bf.tp.[charset | collate | flag]
查看 MySQL 行为可以通过在终端启动$
mysql -uroot \-\-column-type-info,这样对于每一个查询语句,可以查看每一列详细的 metadata
对于返回值类型为 string 的函数,以concat
为例,当存在类型为 string 且包含 binary flag 的参数时,其返回值也应设置 binary flag
b. 对于返回值类型为
Time 的函数,需要注意,根据函数行为,设置
bf.tp.Tp = [ TypeDate | TypeDatetime | TypeTimestamp ] ,若为 TypeDate/ TypeDatetime,还需注意推导 bf.tp.Decimal (即小数位数)
c. 不确定性的函数:
Rand
ConnectionID
CurrentUser
User
Database
Schema
FoundRows
LastInsertId
Version
Sleep
GetVar
SetVar
Values
SessionUser
SystemUser
RowCount
UUID
(2)实现 builtinLengthSig.evalInt() 方法:保持不变,此处请注意修改该函数的注释 (s/ eval/ evalXXX)
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)
}
与上一篇文章保持不变,需要注意的是,为了保证可读性, TestStringBuiltin() 方法仅对 expression/builtin_string.go
文件中的 built-in 函数进行测试。如果 executor_test.go 文件中不存在对应的 TestXXXBuiltin() 方法,可以新建一个对应的测试函数。
func(s*testPlanSuite)TestInferType(c*C){
....
tests:=[]struct{
sql string
tp byte
chs string
flag byte
flen int
decimalint
}{
...
// 此处添加对 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 赠送限量版马克杯的活动还在继续中,任何一个新加入集体的小伙伴都将收到我们充满了诚意的礼物,很荣幸能够认识你,也很高兴能和你一起坚定地走得更远。
提交 PR
PR提交之后,请耐心等待维护者进行 Review。
目前一般在一到两个工作日内都会进行 Review,如果当前的 PR 堆积数量较多可能回复会比较慢。
代码提交后 CI 会执行我们内部的测试,你需要保证所有的单元测试是可以通过的。期间可能有其它的提交会与当前 PR 冲突,这时需要修复冲突。
维护者在 Review 过程中可能会提出一些修改意见。修改完成之后如果 reviewer 认为没问题了,你会收到 LGTM(looks good to me) 的回复。当收到两个及以上的 LGTM 后,该 PR 将会被合并。
合并 PR 后自动成为 Contributor,会收到来自 PingCAP Team 的感谢邮件,请查收邮件并填写领取表单
表单填写地址:http://cn.mikecrm.com/01wE8tX
后台 AI 核查 GitHub ID 及资料信息,确认无误后随即便快递寄出属于你的限量版马克杯
期待你分享自己参与开源项目的感想和经验,TiDB Contributor Club 将和你一起分享开源的力量
了解更多关于 TiDB 的资料请登陆我们的官方网站:https://pingcap.com
加入 TiDB Contributor Club 请添加我们的 AI 微信号:tidbai
TiDB 重构 built-in 函数。截止到目前,得到了来自社区的积极支持与热情反馈,TiDB 参考社区 contributors 的建议,对计算框架进行了部分修改以降低社区同学参与的难度。
本文完成以下2
项工作,希望帮助社区更好的参与进 TiDB 的项目中来:
对尚未重写的 built-in 函数进行陈列
对继上篇文章后,计算框架所进行的修改,进行详细介绍
一. 尚未重写的 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 函数
Coalesce
Greatest
Least
Interval
CaseWhen
If
IfNull
NullIf
AesDecrypt
AesEncrypt
Compress
Decode
DesDecrypt
DesEncrypt
Encode
Encrypt
OldPassword
RandomBytes
SHA1
SHA2
Uncompress
UncompressedLength
ValidatePasswordStrength
Database
FoundRows
CurrentUser
User
ConnectionID
LastInsertID
Version
Benchmark
Charset
Coercibility
Collation
RowCount
Regexp
Abs
Ceil
Floor
Log
Log10
Rand
Pow
Round
Conv
CRC32
Sqrt
Arithmetic
Acos
Asin
Atan
Cot
Exp
PI
Radians
Truncate
Sleep
Lock
ReleaseLock
AnyValue
Default
InetAton
InetNtoa
Inet6Aton
Inet6Ntoa
IsFreeLock
IsIPv4
IsIPv4Prefixed
IsIPv6
IsUsedLock
MasterPosWait
NameConst
ReleaseAllLocks
UUID
UUIDShort
AndAnd
OrOr
LogicXor
BitOp
IsTrueOp
UnaryOp
IsNull
In
Row
SetVar
GetVar
Values
BitCount
Reverse
Convert
Substring
SubstringIndex
Locate
Hex
UnHex
Trim
LTrim
RTrim
Rpad
BitLength
Char
CharLength
FindInSet
Field
MakeSet
Oct
Quote
Bin
Elt
ExportSet
Format
FromBase64
InsertFunc
Instr
LoadFile
Lpad
Date
DateDiff
TimeDiff
DateFormat
FromDays
Hour
Minute
Second
MicroSecond
Month
MonthName
Now
DayName
DayOfMonth
DayOfWeek
DayOfYear
Week
WeekDay
WeekOfYear
Year
YearWeek
FromUnixTime
GetFormat
StrToDate
SysDate
CurrentDate
CurrentTime
Time
UTCDate
UTCTimestamp
Extract
DateArith
TimestampDiff
UnixTimestamp
Timestamp
AddTime
ConvertTz
MakeTime
PeriodAdd
PeriodDiff
Quarter
SecToTime
SubTime
TimeFormat
TimeToSec
TimestampAdd
ToDays
ToSeconds
UTCTim
二. 计算框架进行的修改:
此处依然使用 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{
returnnil,
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))
}
注:
a. 对于返回值类型为
string 的函数,需要,注意参考 MySQL 行为设置 bf.tp.[charset | collate | flag]
查看 MySQL 行为可以通过在终端启动$
mysql -uroot \-\-column-type-info,这样对于每一个查询语句,可以查看每一列详细的 metadata
对于返回值类型为 string 的函数,以concat
为例,当存在类型为 string 且包含 binary flag 的参数时,其返回值也应设置 binary flag
b. 对于返回值类型为
Time 的函数,需要注意,根据函数行为,设置
bf.tp.Tp = [ TypeDate | TypeDatetime | TypeTimestamp ] ,若为 TypeDate/ TypeDatetime,还需注意推导 bf.tp.Decimal (即小数位数)
c. 不确定性的函数:
Rand
ConnectionID
CurrentUser
User
Database
Schema
FoundRows
LastInsertId
Version
Sleep
GetVar
SetVar
Values
SessionUser
SystemUser
RowCount
UUID
(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
decimalint
}{
...
// 此处添加对 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 获赠限量版马克杯,马克杯获取流程如下:
提交 PRPR提交之后,请耐心等待维护者进行 Review。
目前一般在一到两个工作日内都会进行 Review,如果当前的 PR 堆积数量较多可能回复会比较慢。
代码提交后 CI 会执行我们内部的测试,你需要保证所有的单元测试是可以通过的。期间可能有其它的提交会与当前 PR 冲突,这时需要修复冲突。
维护者在 Review 过程中可能会提出一些修改意见。修改完成之后如果 reviewer 认为没问题了,你会收到 LGTM(looks good to me) 的回复。当收到两个及以上的 LGTM 后,该 PR 将会被合并。
合并 PR 后自动成为 Contributor,会收到来自 PingCAP Team 的感谢邮件,请查收邮件并填写领取表单
表单填写地址:http://cn.mikecrm.com/01wE8tX
后台 AI 核查 GitHub ID 及资料信息,确认无误后随即便快递寄出属于你的限量版马克杯
期待你分享自己参与开源项目的感想和经验,TiDB Contributor Club 将和你一起分享开源的力量
了解更多关于 TiDB 的资料请登陆我们的官方网站:https://pingcap.com
加入 TiDB Contributor Club 请添加我们的 AI 微信号:tidbai
相关文章推荐
- 十分钟成为 Contributor 系列 | 重构内建函数进度报告
- 重构系列3.重新组织函数
- 十分钟成为 TiDB Contributor | 添加內建函数
- 重构系列之对象行为的重构:《重构》重新组织函数
- 三十分钟成为 Contributor | 为 TiKV 添加 built-in 函数
- 秒杀多线程第三篇 原子操作 Interlocked系列函数
- ASP 系列函数大全
- PHP:《重构-改善既有代码的设计》之一 重新组织你的函数
- 2016书单总结--重构改善既有代码的设计--重新组织函数
- 机器学习--神经网络算法系列--激活函数
- 重构之重新组织函数
- linux网络编程之POSIX 消息队列 和 系列函数
- linux网络编程之posix 线程(一):线程模型、pthread 系列函数 和 简单多线程服务器端程序
- epoll系列函数使用
- Windows下使用Interlocked系列函数实现的一个轻量级读写锁
- POSIX 消息队列 和 系列函数
- 从零开始学_JavaScript_系列(57)——Generator函数(5)状态机与函数的应用
- 使用SetupDi系列函数进行设备信息的管理
- 深入理解JavaScript系列(2):揭秘命名函数表达式
- 特征点匹配 opencv系列函数解析 追踪相关(一)