LoginSignup
0
0

Go言語のdatabase/sqlパッケージはデータベースドライバーをどのようにして読み込んでいるのか

Last updated at Posted at 2024-01-20

本題

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を呼び出すことで、ユーザーがドライバーを簡単に利用できるように親切に設計されています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0