Откат неудачной миграции Rails - PullRequest
79 голосов
/ 26 марта 2009

Как откатить неудачную миграцию рельсов? Я ожидаю, что rake db:rollback отменит неудачную миграцию, но нет, она откатит предыдущую миграцию (неудачная миграция минус один). И 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: migrating =====================================================
-- add_column(:assets, :test, :integer)
   -> 0.0932s
-- add_column(:asset, :error)
rake aborted!
An error has occurred, all later migrations canceled:

wrong number of arguments (2 for 3)

хорошо, давайте вернемся назад:

$ rake db:rollback
==  AddLevelsToRoles: reverting ===============================================
-- remove_column(:roles, :level)
   -> 0.0778s
==  AddLevelsToRoles: reverted (0.0779s) ======================================

да? это была моя последняя миграция до SimpleTest, а не неудачная миграция. (И было бы неплохо, если бы вывод миграции включал номер версии.)

Итак, давайте попробуем запустить down для неудачной миграции 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: Duplicate column name 'test': ALTER TABLE `assets` ADD `test` int(11)

Неа. Очевидно, что миграция: вниз не работает. Это не сбой, просто не выполняется.

Нет способа избавиться от этой дублированной таблицы, кроме как вручную войти в базу данных и удалить ее, а затем запустить тест. Должен быть лучший способ, чем это.

Ответы [ 9 ]

75 голосов
/ 26 марта 2009

К сожалению, вы должны вручную очистить неудачные миграции для MySQL. MySQL не поддерживает изменения определения транзакционной базы данных.

В Rails 2.2 включены транзакционные миграции для PostgreSQL. Rails 2.3 включает в себя транзакционные миграции для SQLite.

Это на самом деле не поможет вам решить вашу проблему прямо сейчас, но если у вас есть выбор базы данных для будущих проектов, я рекомендую использовать ее с поддержкой транзакционного DDL, потому что она делает миграцию намного приятнее.

Обновление - это все еще актуально в 2017 году, на Rails 4.2.7 и MySQL 5.7, о чем сообщил Алехандро Бабио в другом ответе здесь.

20 голосов
/ 26 марта 2009

Для перехода на указанную версию просто используйте:

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

Но если миграция не удастся частично, вам придется сначала ее очистить. Один из способов будет:

  • отредактируйте down метод миграции, чтобы просто отменить часть up, которая работала
  • мигрировать обратно в предыдущее состояние (с которого вы начали)
  • исправление миграции (включая отмену ваших изменений down)
  • попробуйте еще раз
18 голосов
/ 29 марта 2011

Хорошо, ребята, вот как вы на самом деле это делаете. Я не знаю, о чем говорят вышеупомянутые ответы.

  1. Выясните, какая часть миграции работала. Прокомментируйте это.
  2. Также закомментируйте / удалите часть перенесенной миграции.
  3. Запустите миграцию снова. Теперь он завершит неразрывные части миграции, пропустив уже выполненные части.
  4. Раскомментируйте биты миграции, которые вы закомментировали на шаге 1.

Вы можете перейти вниз и обратно, если хотите убедиться, что у вас это есть прямо сейчас.

12 голосов
/ 10 декабря 2010

Я согласен, что вы должны использовать PostgreSQL, когда это возможно. Однако, когда вы застряли с MySQL, вы можете избежать большинства из этих проблем, попробовав сначала перейти на тестовую базу данных:

rake db:migrate RAILS_ENV=test

Вы можете вернуться к предыдущему состоянию и повторить попытку с

rake db:schema:load RAILS_ENV=test
9 голосов
/ 29 апреля 2015

В 2015 году с Rails 4.2.1 и MySQL 5.7 неудачная миграция не может быть исправлена ​​с помощью стандартных действий rake, которые предоставляет Rails, как это было в 2009 году.

MySql не поддерживает откат DDL-отчетов (в MySQL 5.7 Manual ). И Rails ничего не может с этим поделать.

Также мы можем проверить, как Rails выполняет свою работу: миграция заключена в транзакцию в зависимости от того, как адаптер соединения реагирует на :supports_ddl_transactions?. После поиска этого действия в источнике rails (v 4.2.1) я обнаружил, что только Sqlite3 и PostgreSql поддерживает транзакции, а по по умолчанию это не так поддерживается.

Редактировать Таким образом, текущий ответ на исходный вопрос: сбой миграции MySQL должен быть исправлен вручную.

8 голосов
/ 01 апреля 2009

Простой способ сделать это - обернуть все ваши действия в транзакцию:

class WhateverMigration < ActiveRecord::Migration

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

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

end

Как заметил Люк Франк, «MySql [таблицы MyISAM не поддерживают транзакции]», поэтому вы можете избегать MySQL вообще или, по крайней мере, MyISAM в частности.

Если вы используете MySQL InnoDB, то вышеприведенное будет работать нормально. Любые ошибки в повышении или понижении будут отменены.

BE AWARE некоторые типы действий нельзя отменить с помощью транзакций. Как правило, изменения в таблице (удаление таблицы, удаление или добавление столбцов и т. Д.) Не могут быть отменены.

1 голос
/ 01 мая 2015

Ответ Алехандро Бабио, приведенный выше, дает наилучший текущий ответ.

Еще одна деталь, которую я хочу добавить:

Когда миграция 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 голос
/ 02 марта 2010

У меня была опечатка (в "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 голос
/ 25 января 2010

Запустите только миграцию вниз из консоли:

http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (нажмите на его пирожок)

...