Вопрос

Как откатить неудачную миграцию rails?Я бы ожидал, что 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, а не неудачная миграция.(И, о, было бы неплохо, если бы выходные данные миграции включали номер версии.)

Итак, давайте попробуем выполнить сбой для самой простой миграции:

$ rake db:migrate:down VERSION=20090326173033
$

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

$ 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)

Неа.Очевидно, что migrate: down не сработал.Это не сбой, это просто не выполняется.

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

Это было полезно?

Решение

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

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

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

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

Другие советы

Чтобы перейти к указанной версии, просто используйте:

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

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

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

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

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

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

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

rake db:migrate RAILS_ENV=test

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

rake db:schema:load RAILS_ENV=test

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

MySQL не поддерживает откат статментов DDL (при Руководство пользователя MySQL 5.7).И Rails ничего не может с этим поделать.

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

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

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

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 в частности.

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

БУДЬТЕ ВНИМАТЕЛЬНЫ некоторые типы действий не могут быть отменены с помощью транзакций.Как правило, изменения в таблице (удаление таблицы, удаление или добавление столбцов и т.д.) откату не подлежат.

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

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

У меня была опечатка (в "add_column"):

защитите себя.вверх

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

конец

защитите себя.вниз

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

конец

и тогда ваша проблема (не удается отменить частично неудачную миграцию).после некоторого неудачного поиска в Google я запустил это:

защитите себя.вверх

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

конец

защитите себя.вниз

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

конец

как вы можете видеть, я просто добавил корректирующую линию вручную, а затем снова удалил ее, прежде чем установить ее на место.

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

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

Когда в 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; запрос.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top