web-dev-qa-db-ja.com

失敗したロールバックのロールバックRails migration

失敗したRails移行をどのようにロールバックしますか?rake db:rollbackは失敗した移行を取り消しますが、いいえ、前の移行をロールバックします(失敗した移行から1を引いたもの)。そして、rake db:migrate:down VERSION=myfailedmigrationも機能しません。これに何度か遭遇しましたが、とてもイライラします。以下は、問題を再現するために行った簡単なテストです。

class SimpleTest < ActiveRecord::Migration
  def self.up
    add_column :assets, :test, :integer
    # the following syntax error will cause the migration to fail
    add_column :asset, :test2, :integer
  end

  def self.down
    remove_column :assets, :test
    remove_column :assets, :test2
  end
end

結果:

 == SimpleTest:移行中====================================== ============= 
-add_column(:assets、:test、:integer)
-> 0.0932s 
-add_column(: asset、:error)
 rake aborted!
エラーが発生し、その後の移行はすべてキャンセルされました:
 
間違った数の引数(2で3)

oK、ロールバックしてみましょう:

 $ rake db:rollback 
 == AddLevelsToRoles:reverting ============================= ================= 
-remove_column(:roles、:level)
-> 0.0778s 
 == AddLevelsToRoles:復帰(0.0779s)===================================== 

え?これはSimpleTestの前の最後の移行であり、移行の失敗ではありません。 (ああ、移行の出力にバージョン番号が含まれていればいいですね。)

失敗した移行SimpleTestのダウンを実行してみましょう。

 $ rake db:migrate:down VERSION = 20090326173033 
 $ 

何も起こらず、出力もありません。しかし、とにかく移行を実行したのでしょうか?そのため、SimpleTest移行の構文エラーを修正して、もう一度実行してみてください。

 $ rake db:migrate:up VERSION = 20090326173033 
 == SimpleTest:migrating ======================== ============================ 
-add_column(:assets、:test、:integer)
 rake aborted!
 Mysql :: Error:重複したカラム名 'test':ALTER TABLE `assets` ADD` test` int(11)

いや。明らかに、migrate:downは機能しませんでした。失敗ではなく、実行されていないだけです。

手動でデータベースにアクセスして削除してから、テストを実行する以外に、その重複したテーブルを削除する方法はありません。それよりも良い方法がなければなりません。

80
insane.dreamer

残念ながら、MySQLの失敗した移行を手動でクリーンアップする必要があります。 MySQLは、トランザクションデータベース定義の変更をサポートしていません。

Rails 2.2には、PostgreSQLのトランザクション移行が含まれています。 Rails 2.3にはSQLiteのトランザクション移行が含まれています。

これは今のところあなたの問題の解決には本当に役立ちませんが、将来のプロジェクトでデータベースを選択できる場合は、トランザクションDDLをサポートするデータベースを使用することをお勧めします。

更新-これは2017年も引き続き有効、Rails 4.2.7およびMySQL 5.7、別の回答でAlejandro Babioによって報告されました。

77
Luke Francl

指定したバージョンに移動するには、次を使用します。

rake db:migrate VERSION=(the version you want to go to)

ただし、移行が途中で失敗した場合は、最初にクリーンアップする必要があります。 1つの方法は次のとおりです。

  • 移行のdownメソッドを編集して、機能したupの一部を元に戻す
  • 以前の状態(開始した場所)に移行します
  • 移行の修正(downへの変更の取り消しを含む)
  • 再試行
20
MarkusQ

さて、皆さん、これが実際のやり方です。上記の答えが何を言っているのか分かりません。

  1. アップ移行のどの部分が機能したかを把握します。それらをコメントアウトします。
  2. また、破損した移行の一部をコメント化/削除します。
  3. 移行を再度実行します。これで、移行の破損していない部分が完了し、既に行われた部分はスキップされます。
  4. 手順1でコメントアウトした移行のコメントを外します。

あなたが今それを持っていることを確認したいなら、あなたは再び移行してバックアップすることができます。

18
Simon Woodside

可能であればPostgreSQLを使用する必要があることに同意します。ただし、MySQLにこだわっている場合は、最初にテストデータベースで移行を試みることで、これらの問題のほとんどを回避できます。

rake db:migrate Rails_ENV=test

前の状態に戻って、もう一度試すことができます

rake db:schema:load Rails_ENV=test
12
StefanH

2015年にRails 4.2.1およびMySQL 5.7で、移行の失敗は、2009年のようにRails提供する標準のrakeアクションでは修正できません。 。

MySqlは、DDLステートメントのロールバックをサポートしていません( MySQL 5.7 Manual )。そしてRailsはそれで何もできません。

また、Railsがどのようにジョブを実行しているかを確認できます。移行は トランザクションにラップされています 接続アダプターが:supports_ddl_transactions?。 Rails source(v 4.2.1)でこのアクションを検索した後、トランザクションをサポートしているのは Sqlite および PostgreSql のみであり、 デフォルト ではサポートされていません。

Editしたがって、元の質問に対する現在の答え:失敗したMySQL移行は手動で修正する必要があります。

10
Alejandro Babio

これを行う簡単な方法は、すべてのアクションをトランザクションでラップすることです。

class WhateverMigration < ActiveRecord::Migration

 def self.up
    ActiveRecord::Base.transaction do
...
    end
  end

  def self.down
    ActiveRecord::Base.transaction do
...
    end
  end

end

Luke Franclが述べたように、「MySql [のMyISAMテーブルはトランザクションをサポートしていません」-これが、MySQL全般または少なくともMyISAMを避けることを検討する理由です。

MySQLのInnoDBを使用している場合、上記は問題なく機能します。アップまたはダウンのいずれかのエラーはバックアウトします。

BE AWARE一部のタイプのアクションは、トランザクションを介して元に戻すことはできません。通常、テーブルの変更(テーブルの削除、列の削除または追加など)はロールバックできません。

8
BryanH

コンソールからダウンマイグレーションのみを実行します。

http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (クリックして彼のペーストリーへ)

1
Ivan

タイプミスがありました(「add_column」内):

def self.up

add_column :medias, :title, :text
add_colunm :medias, :enctype, :text

終わり

def self.down

remove_column :medias, :title
remove_column :medias, :enctype   

終わり

そして、あなたの問題(部分的に失敗した移行を元に戻すことはできません)。いくつかのグーグルに失敗した後、私はこれを実行しました:

def self.up

remove_column :medias, :title
add_column :medias, :title, :text
add_column :medias, :enctype, :text

終わり

def self.down

remove_column :medias, :title
remove_column :medias, :enctype

終わり

ご覧のとおり、修正ラインを手動で追加し、チェックインする前に再度削除しました。

1
poster

上記のAlejandro Babioの答えは、現在の最良の答えを提供します。

追加したい追加の詳細:

myfailedmigrationの移行が失敗した場合、適用済みとは見なされません。これは、rake db:migrate:statusを実行することで確認できます。次のような出力が表示されます。

$  rake db:migrate:status
database: sample_app_dev

 Status   Migration ID    Migration Name
--------------------------------------------------
   up      20130206203115  Create users
   ...
   ...
   down    20150501173156  Test migration

失敗した移行で実行されるadd_column :assets, :test, :integerの残留効果は、alter table assets drop column test;クエリを使用してデータベースレベルで元に戻す必要があります。

1
Prakash Murthy