https://github.com/pingcap/tidb/pull/3105
这是第一次PR,一个添加mysql内置函数的活动(活动详情),计算框架都已经设计好了,这里增加了一个MAKEDATE(),基本上就是跟着mysql官方文档的要求写的(https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_makedate)
这是一个入门级的活动,为了熟悉TIDB的开发模式。
https://github.com/pingcap/tidb/pull/3456
第二个PR,这个解决了一个我自己发现的BUG:
当运行这个SQL:
use test; create table t (c varchar(30)); ALTER TABLE `test`.`t` ADD INDEX `index1` ();
会使得TIDB服务崩溃,并且重启后也一直闪退报错。
原因是ALTER TABLE `test`.`t` ADD INDEX `index1` ();这条不合法的DDL语句没有被语法解析器过滤掉,导致读取索引数组时越界了(数组大小0,访问了[0]元素),并且没有回收掉这个panic;然后由于TIDB的DDL异常退出后再下次重启服务时会继续执行,所以服务会无法正常启动了。
解决:在parser解析器里面过滤掉了这个错误的语法即可。
思考流程:
首先根据错误日志:
reorg.go:73: [info] [ddl] run reorg job done panic: runtime error: index out of range goroutine 63 [running]: github.com/pingcap/tidb/ddl.(*ddl).onCreateIndex(0xc4206c81c0, 0xc4206c1ca8, 0xc4200ca4d0, 0x1, 0x1, 0x0) /home/jenkins/workspace/build_tidb_master/go/src/github.com/pingcap/tidb/ddl/index.go:269 +0x78b github.com/pingcap/tidb/ddl.(*ddl).runDDLJob(0xc4206c81c0, 0xc4206c1ca8, 0xc4200ca4d0, 0x0) /home/jenkins/workspace/build_tidb_master/go/src/github.com/pingcap/tidb/ddl/ddl_worker.go:298 +0x51a github.com/pingcap/tidb/ddl.(*ddl).handleDDLJobQueue.func1(0x17675c0, 0xc420298a80, 0xc420298a80, 0x0) /home/jenkins/workspace/build_tidb_master/go/src/github.com/pingcap/tidb/ddl/ddl_worker.go:230 +0x31c github.com/pingcap/tidb/kv.RunInNewTxn(0x175cae0, 0xc4206cc240, 0x455500, 0xc4206c1e48, 0xc420022df0, 0xc420022e30) /home/jenkins/workspace/build_tidb_master/go/src/github.com/pingcap/tidb/kv/txn.go:43 +0xce github.com/pingcap/tidb/ddl.(*ddl).handleDDLJobQueue(0xc4206c81c0, 0xc420057500, 0x0) /home/jenkins/workspace/build_tidb_master/go/src/github.com/pingcap/tidb/ddl/ddl_worker.go:238 +0xff github.com/pingcap/tidb/ddl.(*ddl).onDDLWorker(0xc4206c81c0) /home/jenkins/workspace/build_tidb_master/go/src/github.com/pingcap/tidb/ddl/ddl_worker.go:58 +0x21e created by github.com/pingcap/tidb/ddl.(*ddl).start /home/jenkins/workspace/build_tidb_master/go/src/github.com/pingcap/tidb/ddl/ddl.go:323 +0xea
看到错误抛出在这里:
addIndexColumnFlag(tblInfo, indexInfo) job.SchemaState = model.StatePublic ver, err := updateTableInfo(t, job, tblInfo, originalState) if err != nil { return ver, errors.Trace(err) } // Finish this job. job.State = model.JobDone job.BinlogInfo.AddTableInfo(ver, tblInfo) default: err = ErrInvalidIndexState.Gen("invalid index state %v", tblInfo.State) } return ver, errors.Trace(err) }
https://github.com/pingcap/tidb/blob/72981fdbbd7e7d2c45b47f031d61fb4b3775dfb9/ddl/index.go#L269-L285
而进入addIndexColumnFlag:
func addIndexColumnFlag(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) { col := indexInfo.Columns[0] if indexInfo.Unique && len(indexInfo.Columns) == 1 { tblInfo.Columns[col.Offset].Flag |= mysql.UniqueKeyFlag } else { tblInfo.Columns[col.Offset].Flag |= mysql.MultipleKeyFlag } }
https://github.com/pingcap/tidb/blob/72981fdbbd7e7d2c45b47f031d61fb4b3775dfb9/ddl/index.go#L139-L147
发现这里的indexInfo.Columns长度为0,所以数组越界了。
一开始跟着SQL语句走DEBUG,走到了:
v.err = checkDuplicateColumnName(spec.Constraint.Keys) if v.err != nil { return } default: // Nothing to do now. } case ast.AlterTableOption: for _, opt := range spec.Options { if opt.Tp == ast.TableOptionAutoIncrement { v.err = ErrAlterAutoID return } } default: // Nothing to do now. } } }
这里面是sql编译成语法树后检查语法树合法性的地方。
开始在这里调用了对索引是否为空的检查。
后来coocood 让我在语法解析器里面报错,为了和mysql报相同类型的错误,当时对于这个parser/parser.y 的语法没有接触过,就看了几个其他sql的语法限制,大致理解了语法,然后在:
找到了对索引检查语法的位置,于是在这里添加了:
keys := $5.([]*ast.IndexColName) if len(keys) < 1{ yylex.Errorf("IndexColNameList List can't be empty.") return 1 }
对长度的检查。
添加限制后的代码点击查看。
然后coocood 提醒下发现:这两个限制都是调用了IndexColNameList:
IndexColNameList: { = []*ast.IndexColName{} } | IndexColName { = []*ast.IndexColName{1.(*ast.IndexColName)} } | IndexColNameList ',' IndexColName {= append(1.([]*ast.IndexColName), $3.(*ast.IndexColName)) }
只要移除
对空数组的允许即可。
https://github.com/pingcap/tidb/pull/3533
第三个PR。这个是PingCap最近发起的他们启用了新的计算框架后对所有内置函数的重写活动(详情)的当天提交的。
由于之前交过了MAKEDATE()的内置函数,所以这里用新的框架改写了。
https://github.com/pingcap/tidb/pull/3859
第四个PR。这个修复了一个别人提出的和MYSQL兼容性相关的问题:
在执行语句:
alter table t add column c varchar(4294967295)
时没有检查varchar的最大长度限制(mysql会根据当前表的字符集确定最大限制,TIDB目前还未支持在创建表的时候指定 库->表->字段这样的默认字符集获取,以及tidb支持的字符集有限,所以对已有字符集做了长度限制的支持)。
然后发现还有其他一些格式也需要限制长度(https://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html),所以在这里统一了一个检查长度的方法,然后作为可扩展的标准,以方便以后加入新类型的限制。目前这个PR我已经加了char/varchar/float/set的限制。