Go のブランク識別子を利用した import による pakcage への副作用

ORMを調べていると以下のような import を見かけた。

import _ "github.com/go-sql-driver/mysql"

調べてみると、
これはブランク識別子を利用した import で、
import 対象のパッケージを初期化するためのものらしい。

つまり、
init() や package value として宣言している変数を初期化するために
わざわざ import していることになる。

ただ、なぜ初期化が必要なのかが分からなかった。

以下で書いたようにわざわざブランク識別子で import しなくても、
そのパッケージに依存するパッケージが import してくれるはずなのでは?
pospome.hatenablog.com


調べてみると、
DBのドライバを差し替えるために
ブランク識別子による import が必要であることがわかった。

golang には database/sql というDBを扱うパッケージがある。
これはSQLの発行やトランザクションの実行などを提供するもので、
DB操作はこのパッケージだけで完結する。

そして、DBごとの差異(接続方法の違いなど)はドライバという形で、
別途用意されている。
https://github.com/golang/go/wiki/SQLDrivers#drivers

なので、利用するDBによって、利用者がドライバを import する必要がある。

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql" //mysqlのドライバを利用
)


上記の go-sql-driver/mysql を import すると以下が実行され、
database/sql にドライバがセットされる。
https://github.com/go-sql-driver/mysql/blob/master/driver.go#L182

ドライバがセットされないと、以下でエラーになる。
https://github.com/golang/go/blob/master/src/database/sql/sql.go#L572

Oracle, Postgres を利用したい場合はそれ用のドライバを import すればいい。

ORMは database/sql をラップして
クエリビルダやstructへのマッピングなどの便利機能を提供しているので、
ドライバに影響を受けることはない。
ドライバが Oracle だろーが、Postgres だろーが問題なく動作する。

ただ、ドライバが提供するのは SQL を実行するために最低限必要な実装だけなので、
ORM側で各DBの違いを吸収する必要がなくなるわけではない。

例えば、以下のようなクエリビルダを提供する場合・・・・

orm.Find().Table("tb_test").Limit(10)

MySQL であれば以下のSQLになるが・・・

SELECT * FROM tb_test LIMIT 10;

Oracle であれば以下のSQLになる。

SELECT * FROM tb_test WHERE ROWNUM <= 10;

こういったSQLの方言はORM側で吸収する。

xorm でも各DBごとに実装が存在する。
https://github.com/go-xorm/xorm/blob/master/mssql_dialect.go
https://github.com/go-xorm/xorm/blob/master/mysql_dialect.go
https://github.com/go-xorm/xorm/blob/master/oracle_dialect.go