Откат неудачной миграции 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 должна быть исправлена вручную.
Ответ 7
Запустите только переход вниз с консоли:
http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (щелкните по его пасти)
Ответ 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;
.