ridgeporeのv0.7.6でignoreという属性をカラム・インデックス・FKにつけられるようにした。
あんまり周知していなかったせいで、全然知られていないが、ridgepoleでもexecuteがつかえる。
https://github.com/winebarrel/ridgepole#execute
create_table "authors", force: :cascade do |t| t.string "name", null: false end create_table "books", force: :cascade do |t| t.string "title", null: false t.integer "author_id", null: false end add_index "books", ["author_id"], name: "idx_author_id", using: :btree execute("ALTER TABLE books ADD CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (id)") do |c| # Execute SQL only if there is no foreign key c.raw_connection.query(<<-SQL).each.length.zero? SELECT 1 FROM information_schema.key_column_usage WHERE TABLE_SCHEMA = 'bookshelf' AND CONSTRAINT_NAME = 'fk_author' LIMIT 1 SQL end
冪等性を担保するために少しだけ拡張していて、ブロック内の返り値がtrueの場合のみ、executeを実行するようにしている。 ブロックにはコネクションを渡しているので、存在チェックをすることで重複してexecuteが実行されないようにすることができる。
…が、たとえばN-gramのフルテキストインデックスのように、Railsが対応していないカラムやインデックスをexecuteで作成しようとするとめんどくさいことになる。
例えば
create_table "books", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| t.string "title", null: false t.index ["title"], name: "ft_index", type: :fulltext end execute("CREATE FULLTEXT INDEX ft_index ON books(title) WITH PARSER ngram") do |c| rows = c.raw_connection.query('SHOW INDEX FROM books', as: :hash) !rows.map{|r| r.fetch('Key_name') }.include?('ft_index') end
のような感じでインデックスを貼ろうと思うと、先に「N-gramではないインデックス」が作成されてしまって、「N-gramのインデックス」はexecuteでは作成されなくなる。
executeのブロック内のクエリで頑張って「N-gramではない場合は再作成」というようにすれば、「N-gramのインデックス」を貼ることはできるが、流石にめんどくさいので、カラムやインデックスにignore
という属性をもたせることにした。
ignore
を使うとこんな感じになる
# -*- mode: ruby -*- # vi: set ft=ruby : create_table "books", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| t.string "title", null: false t.index ["title"], name: "ft_index", type: :fulltext, ignore: true end execute("CREATE FULLTEXT INDEX ft_index ON books(title) WITH PARSER ngram") do |c| rows = c.raw_connection.query('SHOW INDEX FROM books', as: :hash) !rows.map{|r| r.fetch('Key_name') }.include?('ft_index') end
…先ほどとほぼ同じだけれど、ignore: true
がついたカラム・インデックス・FKについては、ridgepoleは無視、全く変更に関与しないようにしている(つもり)。
なので、このスキーマ定義はRailsでは対応していない「N-gramのインデックス」を冪等性を保ったまま作成することができる(はず)。
テストが通ったらリリースする予定。