PostgreSQLの場合、Railsのmigrationで追加するカラムの順番を制御するのは難しい

はじめに

簡単に出来るだろうと高をくくっていたら、データベースがPostgreSQLの場合、そうでもなかったというお話です。

動作環境

# rake aboutより一部抜粋
Rails version             4.1.6
Database adapter          postgresql

テーブル構造

以下のようなテーブルで、titleとtypeの間に、descriptionカラムを追加する想定です。

class CreateBooks < ActiveRecord::Migration
  def change
    create_table :books do |t|
      t.string :title # ↓この間にdescriptionを追加したい
      t.integer :type # ↑

      t.timestamps
    end
  end
end

調査開始

RailsAPI

まずはRailsAPIでadd_columnを検索してみます。 オプションの詳細は別ページに記載されているようなので、そこを見てみましたが、それっぽいオプションが見当たりません・・。 あら、もしかして出来ない?・・いや、そんなことはないだろうと、今度はググってみます。

ググる

Qiitaの記事がヒット。ズバリのものを発見。いつもありがとうございます。 どうやらafterオプションを指定してあげれば出来る模様です。

早速、以下のようなマイグレーションファイルを作成し、rake db:migrateで実行! カラムの順番を確認してみると・・あらら、一番最後に追加されているではありませんか・・。 Railsのバージョンが違う?データベースがMySQLだから?と疑問に思いつつもさらにググってみます。

# rails g migration AddDescriptionToBooks description:text
# titleの後ろに追加したいので、after: :titleを指定
class AddDescriptionToBooks < ActiveRecord::Migration
  def change
    add_column :books, :description, :text, after: :title
  end
end

さらにググる

今度はstackoverflowの記事がヒット。むむっ、PostgreSQLでは無効なオプションの様子。 Answerの記事を辿っていくと、PostgreSQLのWikiに到達しました。

MySQLでは簡単に出来ますが、PostgreSQLの場合、かなり面倒な手順を踏まないと出来ない模様です・・。 Wiki曰く、列の順番を変更するには、以下の3通りの方法があるとのこと。

  1. テーブル再作成
  2. 列を追加し、データを移動
  3. ビューを使用した違いの隠ぺい

それぞれの概略はこうです。

  1. テーブル再作成
    1. 求めているカラムの順番で、新しいテーブル(仮にnew_books)を作成
    2. new_booksにbooksのデータをinsert
    3. booksをdrop
    4. new_booksをbooksにリネーム
    5. ビューの参照先やインデックスを再作成
  2. 列を追加し、データを移動
    1. booksにdescriptionを追加
    2. booksにnew_typeを追加
    3. booksにnew_created_atを追加
    4. booksにnew_updated_atを追加
    5. booksのtypeを削除
    6. booksのcreated_atを削除
    7. booksのupdated_atを削除
    8. booksのnew_typeをtypeにリネーム
    9. booksのnew_created_atをcreated_atにリネーム
    10. booksのnew_updated_atをupdated_atにリネーム
    11. ビューの参照先やインデックスを再作成
  3. ビューを使用した違いの隠ぺい
    1. booksにdescriptionを追加
    2. booksをnew_booksにリネーム
    3. 古いテーブルと同じ名前のビュー(books)を作成
    4. 列の更新・挿入・削除を扱うルールを追加

Railsで実現するには?

ということで、add_columnメソッドでは実現できないため 代替手段として、SQLを直接実行する方法を模索します。

Rails GuidのActive Record Migrationsを読んでみると、 3.9 Using reversibleの記載を発見しました。 reversibleメソッド、upメソッドやdownメソッドを利用して、直接SQLを記載すればできそうです。

ただし、今回はそこまでカラムの順番にこだわっている訳ではなく 労力に見合うほどのリターンが返ってくるわけではないと判断したため、結局、単純に最後のカラムに追加することにしました。 よって、具体的な方法については割愛します。

まとめ

データベースがMySQLの場合は、add_columnメソッドにafterオプションを指定すれば、追加するカラムの順番を制御できます。
しかし、PostgreSQLの場合はそうはいかず、直接SQLを実行する等して、自前で頑張る必要があります。