tidb源码学习之ast包

基本结构 ast.go

基本结构

基本实现 base.go

和上图很类似,只是属于内部实现,提供了基本的能力,用于更细化的类型实现。 基本实现

函数调用 functions.go

分别为聚合函数,普通函数,转换函数

函数调用

可视化AST

为了方便理解AST,需要有个简单的可视化工具。

type astViewer struct {
	level int
}

// Enter implements ast.Visitor interface.
func (av *astViewer) Enter(inNode ast.Node) (outNode ast.Node, skipChildren bool) {
	fmt.Println(strings.Repeat("  ", av.level), reflect.TypeOf(inNode))
	av.level++
	return inNode, false
}

// Leave implements ast.Visitor interface.
func (av *astViewer) Leave(inNode ast.Node) (node ast.Node, ok bool) {
	av.level--
	return inNode, true
}

// How to use it.
node.Accept(&astViewer{})

DML语句 dml.go

dml语句类型

dml语句类型

dml语句还有不少内部结构,简单分类一下:

字段相关部分,其中WildCardField是一种特殊的SelectField,FieldList包括多个SelectField

字段

表相关部分,其中从外到里关系是TableRefsClause - Join - TableSource - TableName

表

条件相关部分,就不解释了

条件

DDL语句 ddl.go

ddl

表达式语句 expression.go

函数 functions.go

系统管理语句 misc.go

mysql类型转换之str_to_datetime分析

  • 源码路径 https://github.com/mysql/mysql-server/blob/23032807537d8dd8ee4ec1c4d40f0633cd4e12f9/sql-common/my_time.c
  • 方法 str_to_datetime

解析算法步骤

预处理判断

  1. 跳过开头的空格
  2. 如果剩下为空或者以非数字开头,返回失败
  3. 计算第一部分的内容(包括数字或字符T)
  4. 如果只有一部分,或后面接字符. ,那么属于内部格式

预计年份的长度

  1. 内部格式,长度是4,8,14+的时候,年份是4位,其他是2位
  2. 其他情况,年份是4位

扫描各个部分

  1. 需要重新扫描(第一部分),对每部分进行扫描
  2. 如果是内部格式,扫描固定位数,否则扫描直到非数字,得到这个部分
  3. 如果这部分数值大于999999,返回失败。
  4. 对于每个部分,如果是内部格式,扫描固定长度,否则扫描到出现非数字为止
  5. 每个部分记录得到的数值,原始长度记录长度(可能有前置0),需要记录扫描了几个部分,最后的位置
  6. 如上继续扫描,如果发现了年月日之后,后续可以接字符T,这是特殊情况
  7. 每部分扫描后,跳过中间的间隔,如空格或标点,不同的是,空格不是所有地方都能加,例如年月日后面可以加,年后面就不可以。
  8. 如果扫描到秒这部分,后面如果是符号.的话,还要扫描小数位。

调整结果

  1. 年份原始长度如果是2位,需要做转换处理。
  2. 小数位需要检验长度,对于保留小数位进行处理,可能还要四舍五入
  3. 小数位可能会导致overflow,需要对结果增加一秒
  4. 需要检验日期时间格式各个部分在一定的单位内,特别需要注意闰二月等特殊情况

错误处理

  1. 这个方法很多dirty code,除了返回值,还有flags和warning status
  2. flags用来标记各种解析的选项,会影响解析结果
  3. waring status收集警告信息,有警告可能返回成或失败

tidb源码学习之错误管理

golang错误机制

golang没有内置所谓异常的方式,通常是通过返回一个error参数来标识是否出现错误。不过也提供了一个简单粗暴的panic,recover机制,它只是在应用的框架上使用,绝不是用来控制错误处理流程的。

error是一个简单的接口,只要实现Error方法,就是一个error。自定义实现error,就可以通过判断不同的error类型,来做一些区别对待,例如是忽略并显示警告,还是显示错误。

tidb错误类型

type Error struct {
	class ErrClass
	code ErrCode
	message string
	args []interface{}
	file string
	line int
}

上面是tidb自定义错误的定义,包括了错误类型,错误码,错误消息,还有一些错误位置相关的信息。

它有一个方法ToSQLError,用来转换成对外的错误信息(主要是mysql错误类型和tidb专有的)

转换关系是这样的,可以看看types包中的errors.go的用法,了解实际的使用。

ErrClassToMySQLCodes map[ErrClass](map[ErrCode]uint16)
terrors, trace, sqlerror

从上面的结构可以看出,对于一个Error对象,根据ErrClass和ErrCode是可以对应到一个mysql的错误码的。

错误链的使用

类似java那样,可以包装成另外一个往外抛,最终形成一个错误链,不会丢掉原始的异常信息。原生的error机制太简单了,tidb采用了一个开源库帮忙,https://github.com/juju/errors 通过errors.Trace(err)返回一个包装过的错误或者nil,并且允许通过errors.Cause(err)得到原始的错误,非常方便。

tidb源码学习之测试框架

tidb项目对测试非常重视,采用了大量的测试用例来保证代码的质量,目前代码测试的整体覆盖率在70%左右。

golang自带了测试框架,但tidb实际上使用的是gocheck的测试框架,这个框架兼容官方测试框架的用法。

测试框架介绍路径 gocheck有一些额外的优点,可以在使用过程中慢慢体会。

  • 强大的assert功能
  • 按test suite来组织测试用例,并支持setUp,tearDown
  • 一些便捷功能,如临时文件

下面介绍tidb在测试中一些有趣的用法。在tidb的测试用例里边会用到testkit和testleak两个工具包。

testkit

主要是提供一些便捷的工具,例如查询并检查错误,比较查询结果等,对照使用即可。

testleak

主要是提供了一个检测goroutine泄露的工具。使用很方便,只需要在开头加一个defer语句就可以了。原理也不复杂,就是通过runtime.Stack()得到所有正在运行的goroutine的堆栈,排除一些已知的堆栈,如果还剩下一些未知的,那可能就是有问题了。详细的可以看看tidb的源代码。

健康计划

工作越久,身体就感觉越差,今年特别明显。最近感觉肋部,腹部有些绷紧,上周去做了一下检查,虽然没什么大问题,但尿酸还是持续高涨,已经有好几年的体检都是这个情况了,真是不可以粗心大意,做了一些安排,看自己能坚持多久。

关于吃

  • 尿酸高也属于贵族病的一种,和三高是类似的。吃是要特别注意的。
  • 早上不能再搞红豆粥了,不过可以加个鸡蛋。
  • 早上还是尽量吃了再过去。
  • 平时海鲜也得少吃,虽然我平时也很少吃,可能每次有的时候都比较尽力吃吧。

关于喝

  • 高汤是不能碰了,所以晚上定时的炖汤也得告一段落了。
  • 糖水倒是可以考虑
  • 最最重要的事情就是多喝水了。上班下班得各一壶,其他随传随到,不能少过2000毫升。

关于运动

  • 不适合激烈运动,时间也不是很多,挑选比较简单的运动
  • 偶尔可以摩拜一下,主要是地铁口和家里的一段路
  • 选择在瑜伽垫上做一下支撑,做一些中里巴人推荐的一下锻炼方式
  • 早上晚上都找时间做一下
  • 晚上小小跑个步

关于泡脚

  • 以前还是有坚持小小的泡一下,还是有些效果的
  • 每天晚上还是要泡个20分钟半小时

关于午休

  • 以前我是不午休的,不过自从去年底开始尝试午休之后,感觉下午会有精神些
  • 午休有大半个小时就可以了,午休前不要玩手机什么的