2009-03-26 2 views
77

Как вы откатываете неудачную миграцию рельсов? Я ожидал бы, что rake db:rollback отменит неудачную миграцию, но нет, он откатывает предыдущую миграцию (неудачный переход минус один). И rake db:migrate:down VERSION=myfailedmigration тоже не работает. Я столкнулся с этим несколько раз, и это очень расстраивает. Вот простой тест, который я сделал, чтобы дублировать эту проблему:Откат неудачной миграции Rails

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

Так давайте попробуем запустить вниз для неисправного миграции 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) 

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

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

ответ

73

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

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

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

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

+1

Отлично, спасибо. Я буду делать новые проекты с PGSQL, так что хорошо знать, что это вариант. –

+0

Это по-прежнему лучший ответ, поэтому это заслуживает награду imho. – nathanvda

20

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

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

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

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

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

9

Самый простой способ сделать это, чтобы обернуть все ваши действия в сделке:

class WhateverMigration < ActiveRecord::Migration 

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

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

end 

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

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

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

+5

Это не вопрос MyISAM или InnoDB. InnoDB поддерживает транзакции, но не поддерживает изменения в транзакционной базе данных (DDL). В PostgreSQL вы можете удалить стол, а затем отменить это изменение! –

+1

Люк прав, mysql не поддерживает транзакцию при изменениях DDL. Я должен рассмотреть очистку самостоятельно, например, добавление и удаление столбца из таблиц. –

1

я имел опечатка (в "add_column"):

Защиту self.up

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

конец

Защиту self.down

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

конец

и тогда ваша проблема (не может отменить частично неудачной миграции). после того, как некоторые не смогли прибегая к помощи я управлял этим:

Защиту self.up

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

конец

Защиту self.down

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

конец

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

12

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

rake db:migrate RAILS_ENV=test 

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

rake db:schema:load RAILS_ENV=test 
+0

Больше обходного пути, чем ответа, но это хорошая идея, которая раньше не приходила мне в голову. – Emily

18

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

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

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

+2

Я делаю что-то очень похожее, но я заменяю второй шаг: «Зафиксируйте часть миграции, которая сломалась». –

+1

Стоит подчеркнуть последнюю точку - запустить 'bundle exec rake db: migrate: redo'. Он переместится на один шаг назад и на один шаг вперед, чтобы вы могли убедиться, что ваша последняя миграция проходит весь путь. Это хорошая практика в любое время, когда вы должны перенаправить миграцию вместе с некоторыми обновлениями кода. – mahemoff

8

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

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

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

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

+0

Я не совсем понимаю этот ответ: за исключением обновления номеров версий, он ничего не добавляет к исходному принятому ответу. – nathanvda

+1

Очень верно, для оригинального вопроса. Ибо щедрость началась для Эндрю Гримма: «Хочешь узнать, изменилась ли ситуация с тех пор, как вопрос был задан в марте 2009 года». Это текущий ответ и дает возможность проверить любые изменения в будущем. –

1

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

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

Когда 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;.

Смежные вопросы