本題
Go言語のdatabase/sqlパッケージがデータベースドライバーをどのようにして読み込み、利用しているか気になったので調べてみました。
サンプルコード
import (
"database/sql"
"time"
_ "github.com/go-sql-driver/mysql"
)
// ...
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err)
}
引用) https://github.com/go-sql-driver/mysql
mysqlの場合はこんな感じで読み込むのですが、database/sql.Open
関数の第一引数には"mysql"という文字列しか渡されていなく、ドライバーの実装は渡されていないんですよね。
database/sql.Open
関数の実装を見てみましょう。
func Open(driverName, dataSourceName string) (*DB, error) {
driversMu.RLock()
driveri, ok := drivers[driverName]
driversMu.RUnlock()
if !ok {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
if driverCtx, ok := driveri.(driver.DriverContext); ok {
connector, err := driverCtx.OpenConnector(dataSourceName)
if err != nil {
return nil, err
}
return OpenDB(connector), nil
}
return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil
}
どうやらdrivers
というmapに実装が詰まっていそうです
下のコードがdriversの定義です。
var (
...
drivers = make(map[string]driver.Driver)
)
driversに実装を詰め込むにはRegister関数を使います。
// Register makes a database driver available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
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
}
ですので、
1. Register関数で実装をdriversに詰める
2. Openメソッドでdriversから実装を取り出す
という段階を踏む必要があるのですが、サンプルコードではRegister関数を明示的に呼び出していないにも関わらず、問題なく動作します。
これ実はRegister関数をドライバーパッケージ側からinit時に呼んでいるんです。
以下はドライバーパッケージ側の実装です。
MySQL(go-sql-driver/mysql)
// This variable can be replaced with -ldflags like below:
// go build "-ldflags=-X github.com/go-sql-driver/mysql.driverName=custom"
var driverName = "mysql"
func init() {
if driverName != "" {
sql.Register(driverName, &MySQLDriver{})
}
}
PostgreSQL(lib/pq)
func init() {
sql.Register("postgres", &Driver{})
}
このように、ドライバーパッケージは内部でinit関数を利用してRegisterを呼び出すことで、ユーザーがドライバーを簡単に利用できるように親切に設計されています。