LoginSignup
35
23

More than 5 years have passed since last update.

golang-migrate/migrateをパッケージとしてGoから使ってみる

Last updated at Posted at 2019-02-10

golang-migrate/migrateとは

Goで書かれたデータベースマイグレーションツール
Go製のマイグレーションツールの中では、pressly/gooseと並んで人気がある様子
CLI, Goプロジェクト(パッケージとしてimportして使う)の両方から利用可能

  • レポジトリ
    golang-migrate/migrate
    mattes/migrateをforkしたもので、もともとのmattes/migrateは非推奨になってメンテナンスすでに行われていない

CLIだとインストール環境によってインストールできる、できないとか問題起こってしまったり、コマンドを忘れる/ミスをする可能性も高いので
CLIからではなく、コード化するためにGoのパッケージとして利用してみた備忘録

環境

  • AWS Cloud9 (シンガポールリージョンのEC2上で動作)
  • Amazon Linux2
  • zsh
  • mysql

Goのバージョン

Goのバージョンは1.11.5を使用する
1.11からパッケージマネージャーのdepではなくgo modを使用してモジュールを管理できる
depではまだセマンティックバージョニングに対応していない
golang-migrate/migrateでは、今後優先的にv4のセマンティックバージョンからメンテナンスやアップデートが入るようなので、go modを利用する

sqlファイル(up,down)を作成する

<version>_<name>.up.sql<version>_<name>.down.sqlの2つのsqlファイルを1セットとしてあつかう
たとえば

1_create_table.up.sql
1_create_table.down.sql

このとき、migrateは<version>しかみておらず、<name>の部分は、単純に可読性のみ気にしておけばよいので、sqlの中身がわかるような名前を付けると良さそう

up

変更を加えるsqlファイルを記入する
例えば、create table文やupdateinsert文を書く

down

upで変更を加えた状態から、変更前の状態に戻すクエリを書く
例えば、create tableに対してdrop tableなど

version

1 → 2 → 3 ...のようにインクリメントしていってもよいし
201902101130 → 201902101230 のように日付時刻のタイムスタンプを使ってもよい
ようは、sqlファイルの順番がわかればいいので、昇順になるように設定する

今回はファイルを作成したときの日付時刻を<version>として使うことにした

パッケージをimportしたバッチの作成

migrate.go
package main

import (
    "flag"
    "fmt"
    "github.com/golang-migrate/migrate/v4"
    _ "github.com/golang-migrate/migrate/v4/database/mysql"
    _ "github.com/golang-migrate/migrate/v4/source/file"
    "os"
)

//sql and database info
const (
    Source   = "file://./sql/"
    Database = "mysql://user:password@tcp(0.0.0.0:3306)/database"
)

//declare command line options
var (
    Command = flag.String("exec", "", "set up or down as a argument")
    Force   = flag.Bool("f", false, "force exec fixed sql")
)

//available command list
var AvailableExecCommands = map[string]string{
    "up":      "Execute up sqls",
    "down":    "Execute down sqls",
    "version": "Just check current migrate version",
}

func main() {

    flag.Parse()
    if len(*Command) < 1 {
        fmt.Println("\nerror: no argument\n")
        showUsageMessge()
        os.Exit(1)
        return
    }

    m, err := migrate.New(Source, Database)
    if err != nil {
        fmt.Println("err", err)
    }
    version, dirty, err := m.Version()
    showVersionInfo(version, dirty, err)

    fmt.Println("command: exec", *Command)
    applyQuery(m, version, dirty)
}

//exec up or down sqls
//with force option if needed
func applyQuery(m *migrate.Migrate, version uint, dirty bool) {
    if dirty && *Force {
        fmt.Println("force=true: force execute current version sql")
        m.Force(int(version))
    }

    var err error
    switch *Command {
    case "up":
        err = m.Up()
    case "down":
        err = m.Down()
    case "version":
        //do nothing
        return
    default:
        fmt.Println("\nerror: invalid command '" + *Command + "'\n")
        showUsageMessge()
        os.Exit(1)
    }

    if err != nil {
        fmt.Println("err", err)
        os.Exit(1)
    } else {
        fmt.Println("success:", *Command+"\n")
        fmt.Println("updated version info")
        version, dirty, err := m.Version()
        showVersionInfo(version, dirty, err)
    }
}

func showUsageMessge() {
    fmt.Println("-------------------------------------")
    fmt.Println("Usage")
    fmt.Println("  go run migrate.go -exec <command>\n")
    fmt.Println("Available Exec Commands: ")
    for available_command, detail := range AvailableExecCommands {
        fmt.Println("  " + available_command + " : " + detail)
    }
    fmt.Println("-------------------------------------")
}

func showVersionInfo(version uint, dirty bool, err error) {
    fmt.Println("-------------------")
    fmt.Println("version  : ", version)
    fmt.Println("dirty    : ", dirty)
    fmt.Println("error    : ", err)
    fmt.Println("-------------------")
}

とりあえずupとdownと、ひつようであればforceを使えればよいので、上記のようなコードにしました

up

% go run migrate.go -exec up

down

% go run migrate.go -exec down

-f (force option)

% go run migrate.go -exec up -f

プロジェクト構成

% tree
.
├── create_sql.sh
├── go.mod
├── go.sum
├── migrate.go
└── sql/

なにがベストプラクティスなのか、わからないので
今後、探りながら開発していきたいです

35
23
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
35
23