Revertendo uma migração Rails falhou
-
22-08-2019 - |
Pergunta
Como você reverter uma migração trilhos falhou? Eu esperaria que rake db:rollback
iria desfazer a migração falhou, mas não, ele reverte a migração anterior (a não migração menos um). E rake db:migrate:down VERSION=myfailedmigration
não quer trabalhar. Eu corri para isso algumas vezes e é muito frustrante. Aqui está um teste simples que fiz para duplicar o problema:
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
resultado:
== 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, deixa o rolo de volta:
$ rake db:rollback == AddLevelsToRoles: reverting =============================================== -- remove_column(:roles, :level) -> 0.0778s == AddLevelsToRoles: reverted (0.0779s) ======================================
hein? que foi a minha última migração antes SimpleTest, não houve falha na migração. (E oh, que seria bom se a saída de migração incluído o número da versão).
Então, vamos tentar executar o baixo para a migração falhou SimpleTest:
$ rake db:migrate:down VERSION=20090326173033 $
Nada acontece, e nenhuma saída também. Mas talvez ele correu a migração de qualquer maneira? Então vamos corrigir o erro de sintaxe na migração SimpleTest, e tentar executá-lo novamente.
$ 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)
Não. Obviamente, a migrar: baixo não funcionou. Não é não, ele simplesmente não está em execução.
Não há forma de se livrar do que a tabela duplicado que não vai manualmente no banco de dados e removê-lo e, em seguida, executar o teste. Tem que haver uma maneira melhor do que isso.
Solução
Infelizmente, você deve limpar manualmente migrações falhadas para MySQL. O MySQL não suporta alterações de definição de banco de dados transacionais.
Rails 2.2 inclui migrações transacionais para PostgreSQL. Rails 2.3 inclui migrações transacionais para SQLite.
Isto não realmente ajudá-lo para o seu problema agora, mas se você tem uma escolha de banco de dados sobre projetos futuros, eu recomendo usar um com suporte para DDL transacional porque faz migrações muito mais agradável.
Atualização -. Isso ainda é verdade em 2017, on Rails 4.2.7 e MySQL 5.7, relatado por Alejandro Babio em outra resposta aqui
Outras dicas
Para ir para uma versão especificada apenas usar:
rake db:migrate VERSION=(the version you want to go to)
Mas se a migração falhar parte do caminho, você vai ter que limpá-lo primeiro. Uma forma seria:
- editar o método
down
da migração para apenas desfazer a parte doup
que trabalhou - voltar migrar para o estado anterior (onde começou)
- corrigir a migração (incluindo desfazer as alterações ao
down
) - tente novamente
OK, pessoal, aqui é como você realmente fazê-lo. Eu não sei o que as respostas acima estão falando.
- descobrir qual parte da migração se trabalhou. Comentar os para fora.
- Também comente / remover a parte da migração que quebrou.
- Executar a migração novamente. Agora ele vai concluir as partes não quebrado da migração, ignorando as partes que já foram feitas.
- Uncomment os bits da migração você comentado na etapa 1.
Você pode migrar para baixo e back-up novamente se você quiser verificar se você tem isso agora.
Eu concordo que você deve usar o PostgreSQL, quando possível. No entanto, quando você está preso com o MySQL, você pode evitar a maioria destes problemas por tentar sua migração em seu banco de dados de teste em primeiro lugar:
rake db:migrate RAILS_ENV=test
Você pode reverter para o estado anterior e tente novamente com
rake db:schema:load RAILS_ENV=test
No 2015, com Rails 4.2.1 e MySQL 5.7, a migração falha não pode ser corrigido com ações de rake padrão que Rails fornecem, como era no de 2009.
MySql não suporta reversão de statments DDL (em MySQL 5.7 manual ). E Rails não pode fazer nada com isso.
Além disso, podemos verificar como Rails está fazendo o trabalho: A migração é envolto em uma transação dependendo de como adaptador de ligação responder a :supports_ddl_transactions?
. Depois de uma busca dessa ação na fonte trilhos (v 4.2.1), descobri que só Sqlite3 e PostgreSQL transações suportes, e por padrão não é suportado.
Editar Assim, a resposta atual à pergunta inicial: A falha MySQL migração deve ser corrigido manualmente
.A maneira fácil de fazer isso é envolver todas as suas ações em uma transação:
class WhateverMigration < ActiveRecord::Migration
def self.up
ActiveRecord::Base.transaction do
...
end
end
def self.down
ActiveRecord::Base.transaction do
...
end
end
end
Como Lucas Francl observou, "MySql [ 's tabelas MyISAM Não] operações de apoio" - é por isso que você pode considerar evitando MySQL, em geral, ou pelo menos MyISAM em particular
.Se você estiver usando InnoDB do MySQL, então o acima vai funcionar muito bem. Quaisquer erros em cima ou para baixo vai recuar.
Esteja ciente alguns tipos de acções não podem ser revertidas através de transações. Geralmente, as mudanças de mesa (que deixam cair de uma mesa, remoção ou adição de colunas, etc.) não pode ser revertida.
Executar apenas a migração para baixo do console:
http: //gilesbowkett.blogspot .com / 2007/07 / how-to-use-as migrações-de-console.html (clicar para sua pastie)
Eu tinha um erro de digitação (em "add_column"):
def self.up
add_column :medias, :title, :text add_colunm :medias, :enctype, :text
final
def self.down
remove_column :medias, :title remove_column :medias, :enctype
final
e (migração não pode desfazer parcialmente falhou), então o seu problema. depois de algum falhou googling eu corri o seguinte:
def self.up
remove_column :medias, :title add_column :medias, :title, :text add_column :medias, :enctype, :text
final
def self.down
remove_column :medias, :title remove_column :medias, :enctype
final
como você pode ver eu apenas adicionei a linha correção à mão, e, em seguida, removido-lo novamente, antes que eu verifiquei em.
A resposta de Alejandro Babio acima fornece a melhor resposta atual.
Um detalhe adicional que deseja adicionar:
Quando a migração myfailedmigration
falhar, ele não é considerado como aplicado, e isso pode ser verificada através da execução rake db:migrate:status
, que iria mostrar uma saída semelhante à seguinte:
$ rake db:migrate:status
database: sample_app_dev
Status Migration ID Migration Name
--------------------------------------------------
up 20130206203115 Create users
...
...
down 20150501173156 Test migration
O efeito residual de add_column :assets, :test, :integer
sendo executado sobre a migração não terá que ser revertido no nível de banco de dados com uma consulta alter table assets drop column test;
.