本文档用于描摹如做甚TiDB新增builtin函数。首先引见一些定然的靠山学识,而后引见增加builtin函数的过程,末了会以一个函数做为示例。
靠山学识
SQL语句在TiDB中是怎么施行的。SQL语句首先会通过parser,从文本parse成为AST(笼统语法树),通过optimizer生成施行方案,获得一个能够施行的plan,通过施行这个plan便可获得效果,这期间会波及到怎么猎取table中的数据,怎么对数据停止过滤、计较、排序、围拢、滤重等职掌。关于一个builtin函数,对照重大的是parse和怎么求值。这边偏重说这两部份。
Parse:
TiDB语法剖析的代码在parser目录下,要紧波及misc.go和parser.y两个文献。在TiDB项目中运转makeparser会通过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求值时会移用求值函数。
全体过程
窜改parser/misc.go以及parser/parser.y
在misc.go的tokenMap中增加法则,将函数名剖析为token在parser.y中增加法则,将token序列更动成ast的node在parser_test.go中,增加parser的单位测试make在evaluator包中的求值函数在evaluator/builtin_xx.go中实行该函数的成效,重视这边的函数是遵从典范分了几个文献,譬如工夫干系的函数在。函数的接口为typeBuiltinFuncfunc([]types.Datum,context.Context)(types.Datum,error)并将其name和实行挂号到builtin.Funcs中写单位测试在evaluator目录下,为函数的实行增加单位测试示例这边新增timdiff()扶助的PR为例,停止详细表明
首先看parser/misc.go:
在tokenMap中增加一个entry
这边是界说了一个法则,当觉察文本是timediff时,更动成一个token,token的称号为timediff。SQL对巨细不敏锐,tokenMap内部统一用大写。
关于tokenMap这张内外面的文本,不要被算做identifier,而是做为一个特其余token。接下来在parser法则中,须要对这个token停止特别责罚,看parser/parser.y:
这行的意义是从lexer中拿到timediff这个token后,咱们给他起个名字叫"TIMEDIFF",底下的法则般配时,咱们都操纵这个名字。
这边timediff务必跟tokenMap内部value的timediff对应上,当parser.y生成parser.go的功夫timediff会被授予一个int表率的token编号。
由于timediff不是MySQL的关键字,咱们把法则放在FunctionCallNonKeyword下,
这边的意义是,当scanner输出的token序列餍足这类pattern时,咱们将这些tokens规约为一个新的变量,叫FunctionCallNonKeyword(通过给变量赋值,便可给FunctionCallNonKeyword赋值),也即是一个AST中的node,表率为*ast.FuncCallExpr。其成员变量FnName的值为1的实质,也即是法则中第一个token的value。
至此咱们曾经胜利的将文本"timediff()"更动成为一个ASTnode,其成员FnName纪录了函数名"timediff",用于背面的求值。
假设想引用这个法则中某个token的值,能够用x这类方法,此中x为token在法则中的场所,如上头的法则中,1为"TIMEDIFF",2为’(’,3为’)’。1.(string)的意义是引用第一个场所上的token的值,并断言其值为string表率。
函数挂号在builtin.go中的Funcs表中:
参数表明以下:builtinTimediff:timediff函数的详细实行在builtinTimediff这个函数中2:这个函数起码的参数个数2:这个函数至多的参数个数,语法parse进程中,会检讨参数的个数能否正当
函数实行在builtin_time.go中,一些细节能够看底下的代码以及诠释
末了须要增加单位测试:
散布式NewSQL数据库