十分钟成为 TiDB Contributor,还送限量版马克杯
2019獨角獸企業重金招聘Python工程師標準>>>
背景知識
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 函數:
-
實現函數簽名 接下來要做的事情就是實現 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 parameterarg := args[0]if arg.IsNull() {return d, nil}// 這里對參數值做了一個類型轉換,函數的實現請參考 util/types/datum.gobin, 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 shavar argNull types.Datumf, _ := 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.defaultCharsettp.Flen = 40 {`sha1(123)`, mysql.TypeVarString, "utf8"},{`sha(123)`, mysql.TypeVarString, "utf8"},編輯按:添加 TiDB Robot 微信,加入 TiDB Contributor Club,無門檻參與開源項目,改變世界從這里開始吧(萌萌噠)。
聽說現在成為 TiDB Contributor 就送限量版馬克杯呦~
轉載于:https://my.oschina.net/zhaiyuan/blog/859106
總結
以上是生活随笔為你收集整理的十分钟成为 TiDB Contributor,还送限量版马克杯的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: J2EE的13个规范总结
- 下一篇: CSS——过渡