本文作者:竹翁(杨志丰),毕业于北京大学, OceanBase 内核研发总监。近十年来一直在阿里巴巴/蚂蚁金服自主研发分布式系统和数据库的从事研发工作。
表达式是 SQL 语句的一部分。例如,where employee = lcase('Chang') or id = 1001 中,where 后是一个值类型为 bool 的表达式,其中 lcase 是一个(系统)函数。
在前面《 OceanBase 源码解读(三)SQL 的一生》中讲过,SQL 语句在生成抽象语法树后,经过 resolver 转换解析为 stmt 逻辑执行计划,其中的一个关键部分是表达式的解析。表达式解析类是位于sql/resolver/expr 的ObRawExprResolver,它输入ObParseNode 树,输出表达式树 ObRawExpr 。这个 ObRawExpr 仅用于优化器阶段的语义分析优化,在生成物理执行计划后会被转换为为执行效率优化的 ObExpr 。
上图是表达式解析的主函数。可以看到,抽象语法树结点中用 T_XXX 这样的枚举值表示结点类型。这里有一个比较特别的设计,所有形式为 func(arg1, arg2) 的表达式在 parser 里都被解析为 T_FUN_SYS 类型,函数名存在子结点里,待 resolver 阶段处理。这样,新增一个普通内建函数的时候,我们不需要修改语法文件。不过,这也是为什么有时候相关 SQL 函数不存在的报错与 MySQL , Oracle 的行为不完全一致。从上面代码可以看到,如果内建函数中没找到,还会从用户自定义函数(UDF)中查找。
上图是 process_fun_sys_node() 片段,可以看到,在做了几个特殊的函数别名处理之后,最后通过查找ObExprOperatorFactory 类决定一个函数是否存在。(看这个类还能发现外部没有公开文档的一些隐藏函数,比如研发查问题时常用的 usec_to_time:)
所有内建函数的实现位于 sql/engine/expr 下,类的接口比较清晰,可以照猫画虎。写好实现类后,如上图,用REG_OP 宏把这个类注册一下,表达式解析和代码生成就能自动识别这个新函数。
好了,本期内容到这里就结束了。
那么本系列第二个练习题来了:请你给 OceanBase 社区版实现一个 MySQL 5.7 新增的系统内建函数吧!(这也是 OceanBase SQL 组很多新人入职第一题:)
(未完待续)
附录:前十篇可参考:
1、OceanBase 数据库源码解读(一)引言
2、OceanBase 数据库源码解读(二)目录结构
3、OceanBase 源码解读(三)SQL 的一生
4、OceanBase 源码解读(四)分区的一生
5、OceanBase 源码解读(五)事务的一生
6、OceanBase 源码解读(六)租户的一生
7、OceanBase源码解读(七)带你看透虚拟表
8、OceanBase源码解读(八)OB高性能执行引擎
9、OceanBase源码解读(九)tableAPI和OB多模型
10、OceanBase 源码解读(十)分布式事务