前提
- rails 4.2.5
- winebarrel/ridgepole v0.6.3
その1.export で 外部キー制約の名前が出力されない
問題
例えば以下のような参照整合性が設定されているテーブルがある
# ブログエントリ
class Entry < ActiveRecord::Migration
def change
create_table :entries do |t|
t.timestamps null: false
end
end
end
# 記事に対するコメント
class Comment < ActiveRecord::Migration
def change
create_table :comments do |t|
# 記事テーブルへの外部キー制約が設定されたカラム
t.references :entry, index: true, foreign_key: true
t.timestamps null: false
end
end
end
これを ridgepole で Schemafile にexportすると
create_table "comments", ... do |t|
# comments テーブルの内容
end
create_table "entries", ... do |t|
# entries テーブルの内容
end
add_foreign_key "comments", "entiries"
のように出力されるが、これを ridgepole で apply しようとしても以下のようにエラーとなってしまう。
[ERROR] Foreign key name in `xxxxxxx` is undefined
どうやら、add_foreign_key
メソッドに外部キー制約名(name:
オプション)が抜けているためエラーとなってるらしい。
対処
export結果に外部キーの制約名を出力する pull request は以下で上がっており、つい先日0.6.4ブランチにマージされた模様。
gemとして正式にリリースされているのはまだ v0.6.3 (2016/2/5時点) なので、Gemfileを書き換えて、Githubから直接 "v0.6.4"ブランチをインストールするようにする。
gem "ridgepole", github: "winebarrel/ridgepole", branch: "v0.6.4"
そして、以下のように export 時に --dump-with-default-fk-name
オプションを追加する。
bundle exec ridgepole \
-c '{adapter: mysql2, database: my_database, username: user, password: pass, host: "localhost"}' \
--export -o Schemafile --enable-mysql-awesome --dump-with-default-fk-name
これで外部キー制約の定義以下のように名前付きで出力されるようになる。
add_foreign_key "comments", "entries", name: "fk_rails_xxxxxx"
その2.Schemafileの定義順は(参照先→参照元)でなければならない
※追記(2016/2/9) v0.6.4.beta8 で修正されていることが確認できました。
問題
先の件で外部キー制約の問題は解消したと思われたがまだ問題が発生。
先の Schemafile を ridgepole で apply すると以下のような MySQL のエラーが発生する。
[ERROR] Mysql2::Error: Cannot add foreign key constraint: ALTER TABLE `comments` ADD CONSTRAINT `fk_rails_xxxxx`
どうやら、Schemafile の create_table を順番に実行しているが、その順番が comments (参照元) → entries (参照先) となっており、参照元のテーブルを作成する時点で参照先のテーブルが存在しないためエラーとなっていると思われる。
ridgepole がSchemafileに出力するテーブルの順番はアルファベット順となっており、テーブル間の依存関係は考慮されていないのでそのようなことになるらしい。
対処
これは今のところ対処されていないようなで、export 結果を直接エディタ等で編集して entries (参照先) → comments (参照元) の順番となるように書き換える必要がある。
create_table "entries", ... do |t|
# entries テーブルの内容
end
create_table "comments", ... do |t|
# comments テーブルの内容
end
add_foreign_key "comments", "entiries"
結論
Railsでの外部キー制約はいろいろ面倒くさい。