Откат неудачной миграции 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)

ok, верните его назад:

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

rake db:migrate RAILS_ENV=test

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

rake db:schema:load RAILS_ENV=test

Ответ 5

Простым способом сделать это - все транзакции в транзакции:

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, то приведенное выше будет работать нормально. Любые ошибки в верхней или нижней части будут отступать.

ОЗНАКОМИТЬСЯ некоторые действия не могут быть возвращены с помощью транзакций. Как правило, изменения таблицы (удаление таблицы, удаление или добавление столбцов и т.д.) Не могут быть отброшены назад.

Ответ 6

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

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

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

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

Ответ 8

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

конец

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

Ответ 9

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

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

При неудачной миграции 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;.