Goose для миграций
Программист, приученный к миграциям, это как кот, приученный к лотку - где попало гадить не будет. В проекте, который разрабатывается на больше чем на одной машине, миграции нужны как воздух.
Есть много разных инструментов для миграций, написанные на других языках. На Go тоже есть. Правда поменьше и попроще. Например goose.
Устанавливается утилита через go get:
$ go get bitbucket.org/liamstask/goose/cmd/goose
Почитать о том, как использовать goose можно на страничке проекта на битбакете.
Тулза почти идеальная. Но мне нужно было использовать ее совместно с SQLite, а доступные диалекты только mysql или potsgre, что меня никак не устраивало.
Поддержка SQLite
Полезем внутря. Первым делом в файле goose/lib/goose/dialect.go добавляем новый диалект:
func dialectByName(d string) SqlDialect {
switch d {
case "postgres":
return &PostgresDialect{}
case "mysql":
return &MySqlDialect{}
case "sqlite3":
return &SqliteDialect{}
}
return nil
}
И добавляем новый тип SqliteDialect, который соответствует интерфейсу SqlDialect
////////////////////////////
// sqlite
////////////////////////////
type SqliteDialect struct{}
func (m *SqliteDialect) createVersionTableSql() string {
return `create table goose_db_version (
id integer not null primary key autoincrement,
version_id integer not null,
is_applied integer not null,
tstamp text null
);`
}
func (m *SqliteDialect) insertVersionSql() string {
return "INSERT INTO goose_db_version (version_id, is_applied) VALUES (?, ?);"
}
func (m *SqliteDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
rows, err := db.Query("SELECT version_id, is_applied from goose_db_version ORDER BY id DESC")
// XXX: check for mysql specific error indicating the table doesn't exist.
// for now, assume any error is because the table doesn't exist,
// in which case we'll try to create it.
if err != nil {
return nil, ErrTableDoesNotExist
}
return rows, err
}
Теперь, в файле goose/lib/goose/dbconf.go указываем настройки для sqlite: какой драйвер и какой тип диалекта юзать:
// Create a new DBDriver and populate driver specific
// fields for drivers that we know about.
// Further customization may be done in NewDBConf
func newDBDriver(name, open string) DBDriver {
d := DBDriver{
Name: name,
OpenStr: open,
}
switch name {
case "postgres":
d.Import = "github.com/lib/pq"
d.Dialect = &PostgresDialect{}
case "mymysql":
d.Import = "github.com/ziutek/mymysql/godrv"
d.Dialect = &MySqlDialect{}
case "sqlite3":
d.Import = "github.com/mattn/go-sqlite3"
d.Dialect = &SqliteDialect{}
}
return d
}
В файле goose/lib/goose/migrate.go нужно предварительно указать в импорте дравер для sqlite.
import (
//...
_ "github.com/mattn/go-sqlite3"
//...
)
И на этом все. Теперь можно использовать goose для рботы с sqlite.
Только одно маленькое замечание. В отличие от драйверов postgres и mysql, драйвер sqlite не будет выполнять запросы при использовании txn.Query(). Нужно использовать txn.Exec()