web-dev-qa-db-fra.com

Annulation d'une migration Rails échouée)

Comment annuler une migration Rails échouée? Je m'attendrais à ce que rake db:rollback annulerait la migration ayant échoué, mais non, elle annule la migration précédente (la migration ayant échoué moins une). Et rake db:migrate:down VERSION=myfailedmigration ne fonctionne pas non plus. Je l'ai rencontré plusieurs fois et c'est très frustrant. Voici un test simple que j'ai fait pour dupliquer le problème:

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

résultat:

 == SimpleTest: migration ========================================= ============= 
 - add_column (: assets,: test,: integer) 
 -> 0,0932s 
 - add_column (: actif,: erreur) 
 râteau abandonné! 
 Une erreur s'est produite, toutes les migrations ultérieures ont été annulées: 
 
 nombre incorrect d'arguments (2 pour 3) 

ok, permet de revenir en arrière:

 $ rake db: rollback 
 == AddLevelsToRoles: retour ================================ ================= 
 - remove_column (: rôles,: niveau) 
 -> 0,0778s 
 == AddLevelsToRoles: inversé (0,0779 s) ====================================== 

hein? c'était ma dernière migration avant SimpleTest, pas l'échec de la migration. (Et oh, ce serait bien si la sortie de la migration incluait le numéro de version.)

Essayons donc d'exécuter le down pour l'échec de la migration SimpleTest:

 $ rake db: migrate: down VERSION = 20090326173033 
 $ 

Rien ne se passe et aucune sortie non plus. Mais peut-être qu'il a quand même exécuté la migration? Permet donc de corriger l'erreur de syntaxe dans la migration SimpleTest et d'essayer de l'exécuter à nouveau.

 $ rake db: migrate: up VERSION = 20090326173033 
 == SimpleTest: migration ========================== ============================ 
 - add_column (: assets,: test,: integer) 
 rake avorté! 
 Mysql :: Erreur: nom de colonne en double 'test': ALTER TABLE `assets` ADD` test` int (11) 

Nan. Évidemment, la migration: le down n'a pas fonctionné. Ce n'est pas un échec, ce n'est tout simplement pas une exécution.

Aucun moyen de se débarrasser de cette table en double autre que d'aller manuellement dans la base de données et de la supprimer, puis d'exécuter le test. Il doit y avoir un meilleur moyen que ça.

80
insane.dreamer

Malheureusement, vous devez nettoyer manuellement les migrations ayant échoué pour MySQL. MySQL ne prend pas en charge les modifications de définition de base de données transactionnelles.

Rails 2.2 inclut des migrations transactionnelles pour PostgreSQL. Rails 2.3 inclut les migrations transactionnelles pour SQLite.

Cela ne vous aide pas vraiment pour votre problème en ce moment, mais si vous avez le choix d'une base de données sur de futurs projets, je vous recommande d'en utiliser une avec le support de DDL transactionnel car cela rend les migrations beaucoup plus agréables.

Mise à jour - cela est toujours vrai en 2017, sur Rails 4.2.7 et MySQL 5.7, rapporté par Alejandro Babio dans une autre réponse ici.

77
Luke Francl

Pour accéder à une version spécifiée, utilisez simplement:

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

Mais si une migration échoue à mi-chemin, vous devrez d'abord la nettoyer. Une façon serait:

  • éditez la méthode down de la migration pour simplement annuler la partie de up qui a fonctionné
  • revenir à l'état précédent (là où vous avez commencé)
  • corriger la migration (y compris l'annulation de vos modifications dans down)
  • réessayer
20
MarkusQ

OK, les amis, voici comment vous le faites. Je ne sais pas de quoi parlent les réponses ci-dessus.

  1. Déterminez quelle partie de la migration ascendante a fonctionné. Commentez-les.
  2. Mettez également en commentaire/supprimez la partie de la migration qui s'est interrompue.
  3. Relancez la migration. Maintenant, il terminera les parties non interrompues de la migration, en ignorant les parties qui ont déjà été effectuées.
  4. Décommentez les bits de la migration que vous avez commentés à l'étape 1.

Vous pouvez migrer vers le bas et sauvegarder à nouveau si vous souhaitez vérifier que vous l'avez maintenant.

18
Simon Woodside

J'accepte que vous utilisiez PostgreSQL lorsque cela est possible. Cependant, lorsque vous êtes bloqué avec MySQL, vous pouvez éviter la plupart de ces problèmes en essayant d'abord votre migration sur votre base de données de test:

rake db:migrate Rails_ENV=test

Vous pouvez revenir à l'état précédent et réessayer avec

rake db:schema:load Rails_ENV=test
12
StefanH

En 2015 avec Rails 4.2.1 et MySQL 5.7, une migration échouée ne peut pas être corrigée avec des actions de râteau standard fournies par Rails comme en 2009) .

MySql ne prend pas en charge la restauration des relevés DDL (à MySQL 5.7 Manual ). Et Rails ne peut rien faire avec ça.

En outre, nous pouvons vérifier comment Rails fait le travail: une migration est encapsulée dans une transaction selon la façon dont l'adaptateur de connexion répond à :supports_ddl_transactions?. Après une recherche de cette action sur Rails source (v 4.2.1), j'ai trouvé que seulement Sqlite et PostgreSql supporte les transactions, et par par défaut il n'est pas pris en charge.

Modifier Ainsi, la réponse actuelle à la question d'origine: Une migration MySQL ayant échoué doit être corrigée manuellement.

10
Alejandro Babio

La façon la plus simple de procéder consiste à encapsuler toutes vos actions dans une transaction:

class WhateverMigration < ActiveRecord::Migration

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

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

end

Comme l'a noté Luke Francl, "les tables MyISql de MySql ne prennent pas en charge les transactions" - c'est pourquoi vous pourriez envisager d'éviter MySQL en général ou au moins MyISAM en particulier.

Si vous utilisez InnoDB de MySQL, alors ce qui précède fonctionnera très bien. Toutes les erreurs de montée ou de descente seront annulées.

ATTENTION certains types d'actions ne peuvent pas être annulés via des transactions. Généralement, les modifications de table (suppression d'une table, suppression ou ajout de colonnes, etc.) ne peuvent pas être annulées.

8
BryanH

Exécutez uniquement la migration vers le bas à partir de la console:

http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (cliquez pour accéder à son pastie)

1
Ivan

J'ai eu une faute de frappe (dans "add_column"):

def self.up

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

fin

def self.down

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

fin

puis votre problème (ne peut pas annuler la migration partiellement échouée). après un googling raté, j'ai exécuté ceci:

def self.up

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

fin

def self.down

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

fin

comme vous pouvez le voir, je viens d'ajouter la ligne de correction à la main, puis de la retirer à nouveau, avant de l'enregistrer.

1
poster

La réponse d'Alejandro Babio ci-dessus fournit la meilleure réponse actuelle.

Un détail supplémentaire que je veux ajouter:

Lorsque la migration myfailedmigration échoue, elle n'est pas considérée comme appliquée, et cela peut être vérifié en exécutant rake db:migrate:status, qui afficherait une sortie similaire à la suivante:

$  rake db:migrate:status
database: sample_app_dev

 Status   Migration ID    Migration Name
--------------------------------------------------
   up      20130206203115  Create users
   ...
   ...
   down    20150501173156  Test migration

L'effet résiduel de add_column :assets, :test, :integer en cours d'exécution sur l'échec de la migration devra être inversé au niveau de la base de données avec un alter table assets drop column test; requete.

1
Prakash Murthy