十分鐘成爲 Contributor 系列 | 重構內建函數進度報告


title: 十分鐘成爲 Contributor 系列 | 重構內建函數進度報告
author: 徐懷宇
date: 2017-07-14
summary: 爲了方便社區同窗更好地參與 TiDB 項目,本文一方面對繼上一篇文章發佈後參考社區的反饋對錶達式計算框架所作的修改進行詳細介紹,另外一方面對還沒有重寫的 built-in 函數進行陳列。mysql

tags: TiDB Contributor

6 月 22 日,TiDB 發佈了一篇如何十分鐘成爲 TiDB Contributor 系列的第二篇文章,向你們介紹如何爲 TiDB 重構 built-in 函數。git

截止到目前,獲得了來自社區的積極支持與熱情反饋,TiDB 參考社區 contributors 的建議,對計算框架進行了部分修改以下降社區同窗參與的難度。github

本文完成如下2 項工做,但願幫助社區更好的參與進 TiDB 的項目中來:sql

  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 函數express

0 1 2 3 4 5
Coalesce Uncompress Log10 Default UnaryOp SysDate
Greatest UncompressedLength Rand InetAton IsNull CurrentDate
Least ValidatePasswordStrength Pow InetNtoa In CurrentTime
Interval Database Round Inet6Aton Row Time
CaseWhen FoundRows Conv Inet6Ntoa SetVar UTCDate
If CurrentUser CRC32 IsFreeLock GetVar UTCTimestamp
IfNull User Sqrt IsIPv4 Values Extract
NullIf ConnectionID Arithmetic IsIPv4Prefixed BitCount DateArith
AesDecrypt LastInsertID Acos IsIPv6 Reverse TimestampDiff
AesEncrypt Version Asin IsUsedLock Convert UnixTimestamp
Compress Benchmark Atan MasterPosWait Substring TimeToSec
Decode Charset Cot NameConst SubstringIndex TimestampAdd
DesDecrypt Coercibility Exp ReleaseAllLocks Locate ToDays
DesEncrypt Collation PI UUID Hex ToSeconds
Encode RowCount Radians UUIDShort UnHex UTCTim
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

二. 計算框架進行的修改:

此處依然使用 Length 函數( expression/builtin_string.go )爲例進行說明,與前文采起相同目錄結構:微信

1. expression/builtin_string.go框架

(1)lengthFunctionClass.getFunction() 方法: 簡化類型推導實現 dom

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 的資料請登錄咱們的官方網站:pingcap.com

加入 TiDB Contributor Club 請添加咱們的 AI 微信:微信號:tidbai

相關文章
相關標籤/搜索