最近新しくサービスを作ろうとしてまして、そのサービスのデータベースのスキーマ管理にMiguを使おうとしました。しかし、あまりにも機能が足りていなくて困ったのでYak shavingした結果、Miguを大幅に改良し、0.2.0として約2年越しにリリースしました。リリースと言ってもタグ付けただけですが。

https://github.com/naoina/migu

MiguについてはリポジトリのREADMEか、以前の記事を見てください。

//+migu が必須になった

Migu 0.2.0ではstructのコメントに+miguというアノテーションがあるstructのみをスキーマとして扱うようになりました。今までのMiguはGoのstructすべてをスキーマとして扱っていました。

//+migu
type User struct {
    Name string
}

type Admin struct {
    Name string
}

この例の場合、MiguはUser structをスキーマとみなしますが、Admin structは単に無視します。

CLIの対象にディレクトリを指定できるようになった

Migu 0.2.0ではディレクトリが指定できるようになりました。ディレクトリが指定された場合はその中にあるファイルがスキーマとして扱われます。

migu sync test_db model/

今までのMiguはschema.goのような単一ファイルにすべてのスキーマを書くことを前提としていました。しかし、実際にモデルを書くときにはmodel/user.gomodel/entry.goのような複数のファイルに分けたりします。複数のファイルをスキーマとしてMiguに食わせたい場合、シェルのfor-inなどを使う必要がありました。

for file in model/*.go; do migu sync test_db $file; done

カラム名を指定できるようになった

Migu 0.2.0ではcolumnタグを使うことによってカラム名を指定できます。

//+migu
type User struct {
    EmailAddress string `migu:"column:email"`
}
CREATE TABLE `user` (
  `email` VARCHAR(255) NOT NULL
)

今までのMiguはGoのstructのフィールド名だけを使ってデータベーステーブルのカラム名を決定していました。

//+migu
type User struct {
    EmailAddress string
}

このようなstructがあった場合、MiguはEmailAddressというフィールドをemail_addressというデータベーステーブルのカラム名にマッピングします。

CREATE TABLE `user` (
  `email_address` VARCHAR(255) NOT NULL
)

今まではこのカラム名を指定する方法がありませんでしたが、Migu 0.2.0ではcolumnsタグにより解決しています。

インデックスのサポート

Migu 0.2.0ではindexタグを使うことによりインデックスもMiguで管理できるようになりました。

//+migu
type User struct {
    Email string `migu:"index"`
}

また、インデックスに好きな名前を付けることもできます。

//+migu
type User struct {
    Email string `migu:"index:user_email_index"`
}

複数のフィールドに同じインデックス名を付けると複合インデックスにできます。

//+migu
type User struct {
    Name  string `migu:"index:name_email_index"`
    Email string `migu:"index:name_email_index"`
}

同様に、ユニークインデックスにも名前が付けられるようになりました。もちろん複合ユニークインデックスにも対応しています。

//+migu
type User struct {
    Name  string `migu:"unique:name_email_unique_index"`
    Email string `migu:"unique:name_email_unique_index"`
}

今までのMiguにはインデックスを作成したり削除したりといったSQLを生成できなかったので、手動でSQLを発行する必要がありました。

Extraフィールドのサポート

Migu 0.2.0では _ という特別なフィールドを使ってカラムを定義できるようになりました。

type Timestamp struct {
    CreatedAt time.Time
    UpdatedAt time.Time
}

//+migu
type User struct {
    Name string
    Timestamp
    _ time.Time `migu:"column:created_at"`
    _ time.Time `migu:"column:updated_at"`
}

Miguは埋め込みフィールドを上手く扱えません。これは、MiguがASTを通してstructを解析しているところからくる制限です。GoのORMの中には埋め込みフィールドを扱えるものがあります。例えば次のようなstruct定義では、CreatedAtUpdatedAtをstructとして切り出し、他のstructに埋め込むことによりstructごとにCreatedAtUpdatedAtを定義しなくていいようにしています。

type Timestamp struct {
    CreatedAt time.Time
    UpdatedAt time.Time
}

//+migu
type User struct {
    Name string
    Timestamp
}

ASTではTimestampというフィールドがCreatedAtUpdatedAtというフィールドを持っていることがわかりません。これはASTがパッケージやファイルを超えた依存関係が解決できないからです。Extraフィールドのサポートはこの問題に対するひとつの回避策です。

きちんと埋め込みフィールドをサポートしたいところなのですが、埋め込みフィールドをサポートしようとすると、どうしてもreflectを使わざるを得なく、reflectを使う場合は一旦ビルドする必要があり、GOPATHimportするパッケージの解決などを考えると、やはり難しいと考えています。もっといいアイデアがあれば教えてください。

テーブルオプションのサポート

Migu 0.2.0では、データベースのテーブル名や作成時に指定できるオプションを指定できるようになりました。

//+migu table:"guest" option:"ROW_FORMAT = DYNAMIC"
type User struct {
    Name string
}

当初はこれが欲しくてMiguの開発を再開しました。utf8mb4 + VARCHARの最大文字数問題を回避するためにROW_FORMAT = DYNAMICを付けたかっただけですが、ひとつ気になると別の場所も気になって、あれよあれよと改良してしまったという経緯があります。

その他

PostgreSQLやSQLite3サポートについて

私自身では対応しない方針です。これは、私が基本的にMariaDB/MySQLしか使わないためメンテナンスができないという理由によるものです。PostgreSQLやSQLite3サポートについてはプルリクエストをもらったら考えます。