成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為 TiDB 重構(gòu) built-in 函數(shù)

企業(yè)動(dòng)態(tài)
本文將首先介紹利用新的表達(dá)式計(jì)算框架重構(gòu) built-in 函數(shù)實(shí)現(xiàn)的流程,然后以一個(gè)函數(shù)作為示例進(jìn)行詳細(xì)說(shuō)明,最后介紹重構(gòu)前后表達(dá)式計(jì)算框架的區(qū)別。

為了加速表達(dá)式計(jì)算速度,最近我們對(duì)表達(dá)式的計(jì)算框架進(jìn)行了重構(gòu),這篇教程為大家分享如何利用新的計(jì)算框架為 TiDB 重寫或新增 built-in 函數(shù)。對(duì)于部分背景知識(shí)請(qǐng)參考這篇文章,本文將首先介紹利用新的表達(dá)式計(jì)算框架重構(gòu) built-in 函數(shù)實(shí)現(xiàn)的流程,然后以一個(gè)函數(shù)作為示例進(jìn)行詳細(xì)說(shuō)明,***介紹重構(gòu)前后表達(dá)式計(jì)算框架的區(qū)別。

一、重構(gòu) built-in 函數(shù)整體流程

1. 在 TiDB 源碼 expression 目錄下選擇任一感興趣的函數(shù),假設(shè)函數(shù)名為 XX

2. 重寫 XXFunctionClass.getFunction() 方法

  • 該方法參照 MySQL 規(guī)則,根據(jù) built-in 函數(shù)的參數(shù)類型推導(dǎo)函數(shù)的返回值類型
  • 根據(jù)參數(shù)的個(gè)數(shù)、類型、以及函數(shù)的返回值類型生成不同的函數(shù)簽名,關(guān)于函數(shù)簽名的詳細(xì)介紹見文末附錄

3. 實(shí)現(xiàn)該 built-in 函數(shù)對(duì)應(yīng)的所有函數(shù)簽名的 evalYY() 方法,此處 YY 表示該函數(shù)簽名的返回值類型

4. 添加測(cè)試:

  • 在 expression 目錄下,完善已有的 TestXX() 方法中關(guān)于該函數(shù)實(shí)現(xiàn)的測(cè)試
  • 在 executor 目錄下,添加 SQL 層面的測(cè)試

5. 運(yùn)行 make dev,確保所有的 test cast 都能跑過(guò)

二、示例

這里以重寫 LENGTH() 函數(shù)的 PR 為例,進(jìn)行詳細(xì)說(shuō)明

1. 首先看 expression/builtin_string.go:

(1)實(shí)現(xiàn) lengthFunctionClass.getFunction() 方法

該方法主要完成兩方面工作: 1. 參照 MySQL 規(guī)則推導(dǎo) LEGNTH 的返回值類型 2. 根據(jù) LENGTH 函數(shù)的參數(shù)個(gè)數(shù)、類型及返回值類型生成函數(shù)簽名。由于 LENGTH 的參數(shù)個(gè)數(shù)、類型及返回值類型只存在確定的一種情況,因此此處沒(méi)有定義新的函數(shù)簽名類型,而是修改已有的 builtinLengthSig,使其組合了 baseIntBuiltinFunc(表示該函數(shù)簽名返回值類型為 int)

  1. type builtinLengthSig struct { 
  2.     baseIntBuiltinFunc 
  3.   
  4. func (c *lengthFunctionClass) getFunction(args []Expression, ctx context.Context) (builtinFunc, error) { 
  5.     // 參照 MySQL 規(guī)則,對(duì) LENGTH 函數(shù)返回值類型進(jìn)行推導(dǎo) 
  6.     tp :types.NewFieldType(mysql.TypeLonglong) 
  7.     tp.Flen = 10 
  8.     types.SetBinChsClnFlag(tp) 
  9.             
  10.     // 根據(jù)參數(shù)個(gè)數(shù)、類型及返回值類型生成對(duì)應(yīng)的函數(shù)簽名,注意此處與重構(gòu)前不同,使用的是 newBaseBuiltinFuncWithTp 方法,而非 newBaseBuiltinFunc 方法 
  11.     // newBaseBuiltinFuncWithTp 的函數(shù)聲明中,args 表示函數(shù)的參數(shù),tp 表示函數(shù)的返回值類型,argsTp 表示該函數(shù)簽名中所有參數(shù)對(duì)應(yīng)的正確類型 
  12.     // 因?yàn)?nbsp;LENGTH 的參數(shù)個(gè)數(shù)為1,參數(shù)類型為 string,返回值類型為 int,因此此處傳入 tp 表示函數(shù)的返回值類型,傳入 tpString 用來(lái)標(biāo)識(shí)參數(shù)的正確類型。對(duì)于多個(gè)參數(shù)的函數(shù),調(diào)用 newBaseBuiltinFuncWithTp 時(shí),需要傳入所有參數(shù)的正確類型 
  13.     bf, err :newBaseBuiltinFuncWithTp(args, tp, ctx, tpString) 
  14.     if err != nil { 
  15.         return nil, errors.Trace(err) 
  16.     } 
  17.     sig := &builtinLengthSig{baseIntBuiltinFunc{bf}} 
  18.     return sig.setSelf(sig), errors.Trace(c.verifyArgs(args)) 

(2) 實(shí)現(xiàn) builtinLengthSig.evalInt() 方法

  1. func (b *builtinLengthSig) evalInt(row []types.Datum) (int64, bool, error) { 
  2.     // 對(duì)于函數(shù)簽名 builtinLengthSig,其參數(shù)類型已確定為 string 類型,因此直接調(diào)用 b.args[0].EvalString() 方法計(jì)算參數(shù) 
  3.     val, isNull, err :b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx) 
  4.     if isNull || err != nil { 
  5.         return 0, isNull, errors.Trace(err) 
  6.     } 
  7.     return int64(len([]byte(val))), false, nil 

然后看 expression/builtin_string_test.go,對(duì)已有的 TestLength() 方法進(jìn)行完善:

  1. func (s *testEvaluatorSuite) TestLength(c *C) { 
  2.     defer testleak.AfterTest(c)() // 監(jiān)測(cè) goroutine 泄漏的工具,可以直接照搬 
  3.     // cases 的測(cè)試用例對(duì) length 方法實(shí)現(xiàn)進(jìn)行測(cè)試 
  4.     // 此處注意,除了正常 case 之外,***能添加一些異常的 case,如輸入值為 nil,或者是多種類型的參數(shù) 
  5.     cases := []struct { 
  6.         args     interface{} 
  7.         expected int64 
  8.         isNil    bool 
  9.         getErr   bool 
  10.     }{ 
  11.         {"abc", 3, false, false}, 
  12.         {"你好", 6, false, false}, 
  13.         {1, 1, false, false}, 
  14.         ... 
  15.     } 
  16.     for _, t :range cases { 
  17.         f, err :newFunctionForTest(s.ctx, ast.Length, primitiveValsToConstants([]interface{}{t.args})...) 
  18.         c.Assert(err, IsNil) 
  19.         // 以下對(duì) LENGTH 函數(shù)的返回值類型進(jìn)行測(cè)試 
  20.         tp :f.GetType() 
  21.         c.Assert(tp.Tp, Equals, mysql.TypeLonglong) 
  22.         c.Assert(tp.Charset, Equals, charset.CharsetBin) 
  23.         c.Assert(tp.Collate, Equals, charset.CollationBin) 
  24.         c.Assert(tp.Flag, Equals, uint(mysql.BinaryFlag)) 
  25.         c.Assert(tp.Flen, Equals, 10) 
  26.         // 以下對(duì) LENGTH 函數(shù)的計(jì)算結(jié)果進(jìn)行測(cè)試 
  27.         d, err :f.Eval(nil) 
  28.         if t.getErr { 
  29.             c.Assert(err, NotNil) 
  30.         } else { 
  31.             c.Assert(err, IsNil) 
  32.             if t.isNil { 
  33.                 c.Assert(d.Kind(), Equals, types.KindNull) 
  34.             } else { 
  35.                 c.Assert(d.GetInt64(), Equals, t.expected) 
  36.             } 
  37.         } 
  38.     } 
  39.     // 以下測(cè)試函數(shù)是否是具有確定性 
  40.     f, err :funcs[ast.Length].getFunction([]Expression{Zero}, s.ctx) 
  41.     c.Assert(err, IsNil) 
  42.     c.Assert(f.isDeterministic(), IsTrue) 

***看 executor/executor_test.go,對(duì) LENGTH 的實(shí)現(xiàn)進(jìn)行 SQL 層面的測(cè)試:

  1. // 關(guān)于 string built-in 函數(shù)的測(cè)試可以在這個(gè)方法中添加 
  2. func (s *testSuite) TestStringBuiltin(c *C) { 
  3.     defer func() { 
  4.         s.cleanEnv(c) 
  5.         testleak.AfterTest(c)() 
  6.     }() 
  7.     tk :testkit.NewTestKit(c, s.store) 
  8.     tk.MustExec("use test") 
  9.   
  10.     // for length 
  11.     // 此處的測(cè)試***也能覆蓋多種不同的情況 
  12.     tk.MustExec("drop table if exists t") 
  13.     tk.MustExec("create table t(a int, b double, c datetime, d time, e char(20), f bit(10))") 
  14.     tk.MustExec(`insert into t values(1, 1.1, "2017-01-01 12:01:01", "12:01:01", "abcdef", 0b10101)`) 
  15.     result :tk.MustQuery("select length(a), length(b), length(c), length(d), length(e), length(f), length(null) from t") 
  16.     result.Check(testkit.Rows("1 3 19 8 6 2 <nil>")) 

三、重構(gòu)前的表達(dá)式計(jì)算框架

TiDB 通過(guò) Expression 接口(在 expression/expression.go 文件中定義)對(duì)表達(dá)式進(jìn)行抽象,并定義 eval 方法對(duì)表達(dá)式進(jìn)行計(jì)算:

  1. type Expression interface{ 
  2.     ... 
  3.     eval(row []types.Datum) (types.Datum, error) 
  4.     ... 

實(shí)現(xiàn) Expression 接口的表達(dá)式包括:

  • Scalar Function:標(biāo)量函數(shù)表達(dá)式
  • Column:列表達(dá)式
  • Constant:常量表達(dá)式

下面以一個(gè)例子說(shuō)明重構(gòu)前的表達(dá)式計(jì)算框架。

 

例如:

  1. create table t ( 
  2.     c1 int, 
  3.     c2 varchar(20), 
  4.     c3 double 
  5.   
  6. select * from t where c1 + CONCAT( c2, c3 < “1.1” ) 

對(duì)于上述 select 語(yǔ)句 where 條件中的表達(dá)式: 在編譯階段,TiDB 將構(gòu)建出如下圖所示的表達(dá)式樹:

TiDB 將構(gòu)建出如下圖所示的表達(dá)式樹

在執(zhí)行階段,調(diào)用根節(jié)點(diǎn)的 eval 方法,通過(guò)后續(xù)遍歷表達(dá)式樹對(duì)表達(dá)式進(jìn)行計(jì)算。

對(duì)于表達(dá)式 ‘<’,計(jì)算時(shí)需要考慮兩個(gè)參數(shù)的類型,并根據(jù)一定的規(guī)則,將兩個(gè)參數(shù)的值轉(zhuǎn)化為所需的數(shù)據(jù)類型后進(jìn)行計(jì)算。上圖表達(dá)式樹中的 ‘<’,其參數(shù)類型分別為 double 和 varchar,根據(jù) MySQL 的計(jì)算規(guī)則,此時(shí)需要使用浮點(diǎn)類型的計(jì)算規(guī)則對(duì)兩個(gè)參數(shù)進(jìn)行比較,因此需要將參數(shù) “1.1” 轉(zhuǎn)化為 double 類型,而后再進(jìn)行計(jì)算。

同樣的,對(duì)于上圖表達(dá)式樹中的表達(dá)式 CONCAT,計(jì)算前需要將其參數(shù)分別轉(zhuǎn)化為 string 類型;對(duì)于表達(dá)式 ‘+’,計(jì)算前需要將其參數(shù)分別轉(zhuǎn)化為 double 類型。

因此,在重構(gòu)前的表達(dá)式計(jì)算框架中,對(duì)于參與運(yùn)算的每一組數(shù)據(jù),計(jì)算時(shí)都需要大量的判斷分支重復(fù)地對(duì)參數(shù)的數(shù)據(jù)類型進(jìn)行判斷,若參數(shù)類型不符合表達(dá)式的運(yùn)算規(guī)則,則需要將其轉(zhuǎn)換為對(duì)應(yīng)的數(shù)據(jù)類型。

此外,由 Expression.eval() 方法定義可知,在運(yùn)算過(guò)程中,需要通過(guò) Datum 結(jié)構(gòu)不斷地對(duì)中間結(jié)果進(jìn)行包裝和解包,由此也會(huì)帶來(lái)一定的時(shí)間和空間開銷。

為了解決這兩點(diǎn)問(wèn)題,我們對(duì)表達(dá)式計(jì)算框架進(jìn)行重構(gòu)。

四、重構(gòu)后的表達(dá)式計(jì)算框架

重構(gòu)后的表達(dá)式計(jì)算框架,一方面,在編譯階段利用已有的表達(dá)式類型信息,生成參數(shù)類型“符合運(yùn)算規(guī)則”的表達(dá)式,從而保證在運(yùn)算階段中無(wú)需再對(duì)類型增加分支判斷;另一方面,運(yùn)算過(guò)程中只涉及原始類型數(shù)據(jù),從而避免 Datum 帶來(lái)的時(shí)間和空間開銷。

繼續(xù)以上文提到的查詢?yōu)槔?,在編譯階段,生成的表達(dá)式樹如下圖所示,對(duì)于不符合函數(shù)參數(shù)類型的表達(dá)式,為其加上一層 cast 函數(shù)進(jìn)行類型轉(zhuǎn)換;

重構(gòu)后的表達(dá)式計(jì)算框架

這樣,在執(zhí)行階段,對(duì)于每一個(gè) ScalarFunction,可以保證其所有的參數(shù)類型一定是符合該表達(dá)式運(yùn)算規(guī)則的數(shù)據(jù)類型,無(wú)需在執(zhí)行過(guò)程中再對(duì)參數(shù)類型進(jìn)行檢查和轉(zhuǎn)換。

五、附錄

1. 對(duì)于一個(gè) built-in 函數(shù),由于其參數(shù)個(gè)數(shù)、類型以及返回值類型的不同,可能會(huì)生成多個(gè)函數(shù)簽名分別用來(lái)處理不同的情況。對(duì)于大多數(shù) built-in 函數(shù),其每個(gè)參數(shù)類型及返回值類型均確定,此時(shí)只需要生成一個(gè)函數(shù)簽名。

2. 對(duì)于較為復(fù)雜的返回值類型推導(dǎo)規(guī)則,可以參考 CONCAT 函數(shù)的實(shí)現(xiàn)和測(cè)試??梢岳?MySQLWorkbench 工具運(yùn)行查詢語(yǔ)句 select funcName(arg0, arg1, ...) 觀察 MySQL 的 built-in 函數(shù)在傳入不同參數(shù)時(shí)的返回值數(shù)據(jù)類型。

3. 在 TiDB 表達(dá)式的運(yùn)算過(guò)程中,只涉及 6 種運(yùn)算類型(目前正在實(shí)現(xiàn)對(duì) JSON 類型的支持),分別是

  • int (int64)
  • real (float64)
  • decimal
  • string
  • Time
  • Duration

4. 通過(guò) WrapWithCastAsXX() 方法可以將一個(gè)表達(dá)式轉(zhuǎn)換為對(duì)應(yīng)的類型。

5. 對(duì)于一個(gè)函數(shù)簽名,其返回值類型已經(jīng)確定,所以定義時(shí)需要組合與該類型對(duì)應(yīng)的 baseXXBuiltinFunc,并實(shí)現(xiàn) evalXX() 方法。(XX 不超過(guò)上述 6 種類型的范圍)

【本文是51CTO專欄機(jī)構(gòu)“PingCAP”的原創(chuàng)文章,轉(zhuǎn)載請(qǐng)聯(lián)系作者本人獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來(lái)源: 51CTO專欄
相關(guān)推薦

2020-05-27 09:30:52

JavaScript重構(gòu)函數(shù)

2020-06-01 08:42:11

JavaScript重構(gòu)函數(shù)

2021-04-21 06:03:32

云函數(shù) Todo 重構(gòu)Vue 客戶

2012-05-15 01:16:19

開發(fā)重構(gòu)Java

2019-09-09 11:40:18

編程函數(shù)開發(fā)

2017-07-11 08:38:38

TiDB數(shù)據(jù)庫(kù)分布式

2017-09-09 22:18:26

Mellanox

2021-08-03 08:13:48

重構(gòu)API代碼

2009-07-21 17:16:34

Scala函數(shù)式指令式

2011-06-07 16:47:28

Android 重構(gòu)

2011-07-07 10:39:07

Cocoa 函數(shù)

2022-04-25 10:04:58

MySQL性能數(shù)據(jù)

2020-07-08 09:28:40

首都在線

2024-09-27 12:04:48

2009-12-03 10:03:07

PHP realpat

2021-01-13 12:17:09

AI數(shù)字化技術(shù)數(shù)字化轉(zhuǎn)型

2013-03-08 10:47:03

重構(gòu)

2020-06-10 08:37:21

JavaScript重構(gòu)技巧

2011-06-03 13:48:18

JavaScript重構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美freesex黑人又粗又大 | 风间由美一区二区三区在线观看 | 日韩中文字幕 | 国产精品毛片久久久久久 | 7777在线视频 | 高清成人免费视频 | 亚洲欧美日韩中文在线 | 男人久久天堂 | 日韩欧美国产不卡 | 日本免费一区二区三区四区 | av电影一区二区 | h在线看| 日韩在线精品视频 | 久久天天躁狠狠躁夜夜躁2014 | 欧美一区二区三区在线 | 一区二区三区视频在线免费观看 | 欧美在线一区二区三区 | 久久久国产一区二区三区四区小说 | 2022国产精品 | 久久久一二三 | 成人精品一区 | 福利一区二区 | 欧美日韩国产一区二区三区 | www.色综合| 日本高清中文字幕 | 国产美女在线看 | 欧美狠狠操| 亚洲欧洲一区 | 大陆一级毛片免费视频观看 | 午夜网| 五月婷婷丁香 | 人人干在线视频 | 欧美大片一区 | 精品一区二区三区在线观看国产 | 午夜影院视频 | 在线天堂免费中文字幕视频 | 午夜视频在线视频 | 日韩欧美国产精品一区二区 | 国产欧美日韩精品一区二区三区 | 97成人在线| 色综合久久88色综合天天 |