背景
在工作中,为了简洁和统一,准备将项目B作为基础库引用到我做的项目A中,项目A和B都引用了database/sql以及github.com/go-sql-driver/mysql。做了一系列代码调整后,项目A编译通过,但是运行的时候报错:
golang panic: sql: Register called twice for driver mysql
调查过程
在database/sql/sql.go中找到了这个报错信息,drivers是个map,如果相同的name出现两次就会触发这个错误信息,也就是说Register函数以相同的name作为参数被调用了两次。
func Register(name string, driver driver.Driver) {
driversMu.Lock()
defer driversMu.Unlock()
if driver == nil {
panic("sql: Register driver is nil")
}
if _, dup := drivers[name]; dup {
panic("sql: Register called twice for driver " + name)
}
drivers[name] = driver
}
在go-sql-driver/mysql/driver.go中有个init函数调用了Register函数,对于合并后的项目A和B来说,意味着这个init函数被调用了两次。由于golang中的init函数只会被自动调用一次,虽然项目A和B都import了mysql这个driver,是不应该导致两次init的两次调用的。
func init() {
sql.Register("mysql", &MySQLDriver{})
}
再次检查了项目A和项目B的结构,发现项目B用到了vendor,而且vendor中包括了go-sql-driver/mysql,而项目A用的是golang安装路径下pkg中的mysql driver。至此,怀疑是因为项目A和B引用了不同路径上的mysql driver导致触发了这个错误信息。
解决
临时把go-sql-driver/mysql从项目B的vendor中移除出去后,项目A编译通过并且运行正常。