TiDB 增加 MySQL 內建函式

2021-09-17 19:27:51 字數 3281 閱讀 9426

本文件用於描述如何為 tidb 新增 builtin 函式。首先介紹一些必需的背景知識,然後介紹增加builtin 函式的流程,最後會以乙個函式作為示例。

sql 語句首先會經過 parser,從文字 parse 成為 ast(抽象語法樹),通過 optimizer 生成執行計畫,得到乙個可以執行的 plan,通過執行這個 plan 即可得到結果,這期間會涉及到如何獲取 table 中的資料,如何對資料進行過濾、計算、排序、聚合、濾重等操作。對於乙個 builtin 函式,比較重要的是 parse 和如何求值。這裡著重說這兩部分。

parse:

tidb語法解析的**在 parser 目錄下,主要涉及 misc.go 和 parser.y 兩個檔案。在 tidb 專案中執行 make parser 會通過 goyacc 將 parser.y 其轉換為 parser.go **檔案。轉換後的 go **,可以被其他的 go **呼叫,執行 parse 操作。

將 sql 語句從文字 parse 成結構化的過程中,首先是通過 scanner,將文字切分為 tokens,每個 tokens 會有 name 和 value,其中 name 在 parser 中用於匹配預定義的規則(parser.y),匹配規則時,不斷的從 scanner 中獲取 token,當能完整匹配上一條規則時,會將匹配上的 tokens 替換為乙個新的變數。同時,在每條規則匹配成功後,可以用 tokens 的 value,構造 ast 中的節點或者是 subtree。對於 builtin 函式來說,一般的形式為 name(args),scanner 中要識別 function 的 name,括號,引數等元素,parser 中匹配預定義的規則,構造出乙個 ast 的 node,這個 node 中包含函式引數、函式求值的方法,用於後續的求值。

求值:

求值過程是根據輸入的引數,以及執行時環境,求出函式或者表示式的值。求值的控制邏輯evaluator/evaluator.go 中。對於大部分 builtin 函式,在 parse 過程中被解析為funccallexpr,求值時首先將 ast.funccallexpr 轉換成 expression.scalarfunction,這時會呼叫 newfunction() 方法(expression/scalar_function.go),通過 fnname 在 builtin.funcs 表(evaluator/builtin.go)中找到對應的函式實現,最後在對 scalarfunction 求值時會呼叫求值函式。

這裡以新增 timdiff() 支援的 pr 為例,見

首先看 parser/misc.go:

在 tokenmap 中增加了乙個 entry

var  = map[string]int
這裡是定義了乙個規則,當發現文字是 timediff 時,轉換成乙個 token,token 的名稱為 timediff。sql 對大小不敏感,tokenmap 裡面統一用大寫。

對於 tokenmap 這張表裡面的文字,不要被當作 identifier,而是作為乙個特別的 token。

再看parser/parser.y:

%token    timediff    "timediff"
這行的意思是從 lexer 中拿到 timediff 這個 token 後,我們給他起個名字叫 「」timediff」,下面的規則匹配時,我們都使用這個名字。

這裡 timediff 必須跟 tokenmap 裡面 value 的 timediff 對應上,當 parser.y 生成 parser.go 的時候 timedif f會被賦予乙個 int 型別的 token 編號。

由於 timediff 不是 mysql 的關鍵字,我們把規則放在 functioncallnonkeyword 下,

|    "timediff" '(' expression ',' expression ')',}

}

這裡的意思是,當 scanner 輸出的 token 序列滿足這種 pattern 時,我們將這些 tokens 規約為乙個新的變數,叫 functioncallnonkeyword (通過給$$變數賦值,即可給functioncallnonkeyword 賦值),也就是乙個 ast 中的 node,型別為 *ast.funccallexpr。其成員變數 fnname 的值為 $1 的內容,也就是規則中第乙個 token 的 value。

至此我們已經成功的將文字 」timediff()」 轉換成為乙個 ast node,其成員 fnname 記錄了函式名 「」timediff」,用於後面的求值。

如果想引用這個規則中某個 token 的值,可以用 $x 這種方式,其中 x 為 token 在規則中的位置,如上面的規則中,$1為 」timediff」,$2為 』(』 , $3 為 』)』 。$1.(string) 的意思是引用第乙個位置上的 token 的值,並斷言其值為 string 型別。

函式註冊在builtin.go中的funcs表中:

ast.timediff:         ,
builtintimediff:該 builtin 函式的實現在 builtintimediff 這個函式中

2:最少的引數個數

2:最多的引數個數,語法parse過程中,會檢查引數的個數是否合法

函式實現在 builtin_time.go 中,一些細節可以看下面的**以及注釋

func builtintimediff(args types.datum, ctx context.context) (d types.datum, err error) 

t2, err := converttogotime(sc, args[1])

if err != nil

var t types.duration

t.duration = t1.sub(t2)

t.fsp = types.maxfsp

d.setmysqlduration(t)

return d, nil

}

最後需要增加單元測試:

func (s *testevaluatorsuite) testtimediff(c *c) ,,}

for _, test := range tests , s.ctx)

c.assert(err, isnil)

c.assert(result.getmysqlduration().string(), equals, test.expectstr)

}}

mysql 檢視內建函式 mysql內建函式

一 字串函式 1.concat s1,s2.sn 把傳入的引數連線成乙個字串 2.insert str,x,y,str 從str的x位置開始,替換y長度的字串為str,select insert abcdefg 2,3,hello 3.lower str upper str 將字串轉換為大寫,小寫 ...

mysql 內建函式

六 mysql函式 使用方法 select 內建函式 6.1 日期和時間函式 1.curdate 和current date 獲取當前日期 使用方法 select curdate 或select current date 2.curtime 和current time 函式 獲取當前時間 使用方法 ...

mysql常用內建函式 mysql常見內建函式

在mysql中有許多內建的函式,雖然功能都能在php 中實現,但巧妙的應用mysql內建函式可以大大的簡化開發過程,提高效率。mysql字串函式 concat string2 連線字串 lcase string2 轉換成小寫 ucase string2 轉換成大寫 length string str...