13
10

More than 5 years have passed since last update.

ridgepoleで外部キー制約付きのテーブルを扱った際にハマった件

Last updated at Posted at 2016-02-05

前提

その1.export で 外部キー制約の名前が出力されない

問題

例えば以下のような参照整合性が設定されているテーブルがある

db/migrate/foo.rb
# ブログエントリ
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すると

Schemafile
create_table "comments", ... do |t|
  # comments テーブルの内容
end

create_table "entries", ... do |t|
  # entries テーブルの内容
end

add_foreign_key "comments", "entiries"

のように出力されるが、これを ridgepole で apply しようとしても以下のようにエラーとなってしまう。

error
[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"ブランチをインストールするようにする。

Gemfile
gem "ridgepole", github: "winebarrel/ridgepole", branch: "v0.6.4"

そして、以下のように export 時に --dump-with-default-fk-name オプションを追加する。

example
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

これで外部キー制約の定義以下のように名前付きで出力されるようになる。

Schemafile
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 (参照元) の順番となるように書き換える必要がある。

修正後のSchemafile
create_table "entries", ... do |t|
  # entries テーブルの内容
end

create_table "comments", ... do |t|
  # comments テーブルの内容
end

add_foreign_key "comments", "entiries"

結論

Railsでの外部キー制約はいろいろ面倒くさい。

13
10
4

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
13
10