How do you roll back a failed rails migration? I would expect that rake db:rollback would undo the failed migration, but no, it rolls back the previous migration (the failed migration minus one). And rake db:migrate:down VERSION=myfailedmigration doesn't work either. I've ran into this a few times and it's very frustrating. Here's a simple test I made to duplicate the problem:
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
result:
== 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, lets roll it back:
$ rake db:rollback
== AddLevelsToRoles: reverting ===============================================
-- remove_column(:roles, :level)
-> 0.0778s
== AddLevelsToRoles: reverted (0.0779s) ======================================
huh? that was my last migration before SimpleTest, not the failed migration. (And oh, it would be nice if the migration output included the version number.)
So lets try running the down for the failed migration SimpleTest:
$ rake db:migrate:down VERSION=20090326173033
$
Nothing happens, and no output either. But maybe it ran the migration anyway? So lets fix the syntax error in the SimpleTest migration, and try to run it again.
$ 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. Obviously the migrate:down didn't work. It's not failing, it's just not executing.
No way to get rid of that duplicate table other than manually going into the database and removing it, and then running the test. There's got to be a better way than that.
Unfortunately, you must manually clean up failed migrations for MySQL. MySQL does not support transactional database definition changes.
Rails 2.2 includes transactional migrations for PostgreSQL. Rails 2.3 includes transactional migrations for SQLite.
This doesn't really help you for your problem right now, but if you have a choice of database on future projects, I recommend using one with support for transactional DDL because it makes migrations much more pleasant.
Update - this is still true in 2017, on Rails 4.2.7 and MySQL 5.7, reported by Alejandro Babio in another answer here.
OK, folks, here's how you actually do it. I don't know what the above answers are talking about.
Figure out which part of the up migration worked. Comment those out.
Also comment out/remove the part of the migration that broke.
Run the migration again. Now it will complete the non-broken parts of the migration, skipping the parts that have already been done.
Uncomment the bits of the migration you commented out in step 1.
You can migrate down and back up again if you want to verify that you've got it right now.
To go to a specified version just use:
rake db:migrate VERSION=(the version you want to go to)
But if a migration fails part way, you'll have to clean it up first. One way would be:
edit the down method of the migration to just undo the part of the up that worked
migrate back to the prior state (where you started)
fix the migration (including undoing your changes to the down)
try again
I agree that you should use PostgreSQL when possible. However, when you are stuck with MySQL, you can avoid most of these problems by trying your migration on your test database first:
rake db:migrate RAILS_ENV=test
You can revert to the previous state and try again with
rake db:schema:load RAILS_ENV=test
At 2015 with Rails 4.2.1 and MySQL 5.7, a failed migration can't be fixed with standard rake actions that Rails provide, as it was at 2009.
MySql does not support rollback of DDL statments (at MySQL 5.7 Manual). And Rails can not do anything with that.
Also, we can check how Rails is doing the job: A migration is wrapped in a transaction depending on how connection adapter respond to :supports_ddl_transactions?. After a search of this action at rails source (v 4.2.1), I found that only Sqlite3 and PostgreSql supports transactions, and by default it is not supported.
Edit
Thus the current answer to the original question: A failed MySQL migration must be manually fixed.
The easy way to do this is to wrap all of your actions in a transaction:
class WhateverMigration < ActiveRecord::Migration
def self.up
ActiveRecord::Base.transaction do
...
end
end
def self.down
ActiveRecord::Base.transaction do
...
end
end
end
As Luke Francl noted, "MySql['s MyISAM tables don't] support transactions" -- which is why you might consider avoiding MySQL in general or at least MyISAM in particular.
If you're using MySQL's InnoDB, then the above will work just fine. Any errors in either up or down will back out.
BE AWARE some types of actions cannot be reverted via transactions. Generally, table changes (dropping a table, removing or adding columns, etc.) cannot be rolled back.
Run just the down migration from the console:
http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (click through to his pastie)
I had a typo (in "add_column"):
def self.up
add_column :medias, :title, :text
add_colunm :medias, :enctype, :text
end
def self.down
remove_column :medias, :title
remove_column :medias, :enctype
end
and then your problem (cannot undo partly failed migration). after some failed googling i ran this:
def self.up
remove_column :medias, :title
add_column :medias, :title, :text
add_column :medias, :enctype, :text
end
def self.down
remove_column :medias, :title
remove_column :medias, :enctype
end
as you can see i just added the correction line by hand, and then removed it again, before i checked it in.
Alejandro Babio's answer above provides the best current answer.
One additional detail I want to add:
When the myfailedmigration migration fails, it is not considered as applied, and this can be verified by running rake db:migrate:status, which would show output similar to the following:
$ rake db:migrate:status
database: sample_app_dev
Status Migration ID Migration Name
--------------------------------------------------
up 20130206203115 Create users
...
...
down 20150501173156 Test migration
The residual effect of add_column :assets, :test, :integer being executed on the failed migration will have to be reversed at the database level with a alter table assets drop column test; query.
Related
I have created a model named Categorie in my Rails App.
Due to a mistake, I needed to remove the model.
I ran
rails d model Categorie
Which removed my existing model.
I have forgotten to run rails g migration DropTable at that moment.
But then, I needed to re-create the Categorie model so i ran :
rails g model Categorie name:string
But when i want to run rake db:migrate i get the following error:
rake aborted!
StandardError: An error has occurred, all later migrations canceled:
Mysql2::Error: Table 'categories' already exists: CREATE TABLE `categories` ...
After that, I've tried to drop the table to redo all the process but it doesn't work, the table is still in schema.rb
I know it's not recommended to do it manually from the file that's why i'd like to know if someone would know something about it. I know it's an idiot mistake but now i don't know how to fix this.
Here is how i've tried to drop the table:
rails g migration DropCategories
def change
drop_table :categories
end
rake db:migrate
I think there is a problem with removing the table because here is the output when i migrate the database:
== 20170509123739 CreateCategories: migrating =================================
-- create_table(:categories)
rake aborted!
StandardError: An error has occurred, all later migrations canceled:
Mysql2::Error: Table 'categories' already exists: CREATE TABLE `categories` (`id` int(11) auto_increment PRIMARY KEY, `name` varchar(255), `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL) ENGINE=InnoDB
Rails seems to CREATE a table and not DELETE the one i want.
You can simply drop the table if it exists in your new migration:
def change
reversible do |dir|
dir.up do
drop_table :categories if table_exists? :categories # pre rails 5
drop_table :categories, if_exists: true # rails 5 and onwards
end
end
# put the rest of your migration here:
create_table :categories do |t|
t.string :name
t.timestamps
end
end
The reversible bit ensures that the custom table deletion code is only executed when running the migration normally, and is ignored whenever you do a rollback.
Follow these steps to fix the issue
in your migration file comment the code in 'change' method
def change
create_table :categories do |t|
#t.string :name
#t.timestamps
end
end
run rake db:migrate command
uncomment the code in 'change' method
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
Now run rake db:migrate
Now your migrations get restored to normal and you can continue what you need
--------------------------EDIT #1--------------------------
I found a way around my problem:
The Problem:
I typo'd "admin_user", when it should have been "admin_users" in my migration, and ran rake db:migrate. Giving an ERROR before completing the migration.
A Solution:
rake db:drop #drops the database
rake db:create #re-creates the database
A Question:
How would I have fixed migrating a typo without dropping my whole database and re-creating it? Obviously I don't want to be deleting data with a real project.
EDIT #2
Answer to my question^:
You can go into your code and comment the code that has been already migrated, which will finish the rest of your migration when you run:
rake db:migrate
END OF EDIT #2
--------------------------END OF EDIT #1--------------------------
I'm coming across a noobie MySQL problem using Ruby On Rails 4.
class AlterUsers < ActiveRecord::Migration
def up
rename_table("users", "admin_users")
add_column("admin_users", "username", :string, :limit => 25, :after => "email")
change_column("admin_users", "email", :string, :limit => 100)
rename_column("admin_users", "password", "hashed_password")
puts "*** Adding an index is next ***"
add_index("admin_users", "username")
end
def down
remove_index("admin_users", "username")
rename_column("admin_users", "hashed_password", "password")
change_column("admin_users", "email", :string, :default => "", :null => false)
remove_column("admin_users", "username")
rename_table("admin_users", "users")
end
end
Then I ran:
rake db:migrate
Which then migrated "Create Users", but gave me an ERROR when hitting the Alter Users Migration:
*** Adding an index is next ***
-- add_index("admin_user", "username")
rake aborted!
StandardError: An error has occurred, all later migrations canceled:
Mysql2::Error: Table 'simple_cms_development.admin_user' doesn't exist: CREATE INDEX `index_admin_user_on_username` ON `admin_user` (`username`)
I then tried:
rake db:migrate:status
Which resulted in:
up 20150911203937 Do nothing yet
up 20150911204213 Create users
down 20150911212536 Alter users
So I figured if I just reverted back to all DOWN status by using:
rake db:migrate VERSION=0
But then gave me another Error:
Mysql2::Error: Unknown table 'users': DROP TABLE `users`
/db/migrate/20150911204213_create_users.rb:16:in `down'
Obviously this is just a practice Database and RoR Project in general, but how can I fix this Error and in the BEST possible way so I won't delete anything important (in future SQL projects).
I've read these threads before posting here:
http://stackoverflow.com/questions/24988889/mysql2error-table-conversations-already-exists
http://stackoverflow.com/questions/27876009/ruby-on-rails-mysql2error-table-pages-already-exists-create-table-pages
Thank you for any answers/advice!
Your question doesn't make sense, your error (and apparent db:migrate output) show a problem because there's no "admin_user" (singular) table, and yet your migration shows add_index("admin_users"... (plural). On top of that the error in the subject of your question is never mentioned in your post. Definitely seems like you've gotten yourself in a mess, my recommendation is to drop the database and start from scratch.
I have had ongoing trouble migrating a database in rails via rake db:migrate.
My migration currently looks like this:
class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :title
t.text :subtitle
t.string :slug
t.text :body
t.integer :publish, limit: 1, default: 0
t.timestamps
end
end
end
However if ever I delete a column from this or even add or modify one the rake db:migrate command does nothing. The only way I can migrate it is sometimes running something like:
rake db:migrate VERSION=20080906120000
But even that is temperamental so most of the time I need to reset the db using
db:drop
db:create
then running the migration again as normal. basically db:migrate only ever works the first time after dropping and creating the db.
I've also tried rollback before running the migration.
This is far from ideal so I'd appreciate any help.
(I realise there are similar questions but all of their issues are solved after a db reset)
You need to run the migration down and then run it up again, which is done with the redo task. try this:
rake db:migrate:redo VERSION=20080906120000
EDIT: if you have data you want to keep in this table, it's better to make a new migration to remove the column or whatever you want to do, as the above will drop and recreate the table, wiping the data in the process.
I have created the following Active Record Migration that adds and removes some indexes.
class FixIndexes < ActiveRecord::Migration
def change
add_index :table1, :field1, :unique => true
remove_index :table2, :name => "index_table2_on_field1"
remove_index :table2, :name => "index_table2_on_field2"
remove_index :table3, :name => "index_table3_on_field1"
add_index :table3, [:field1, :field2]
end
end
When I run the migration ($ bundle exec rake db:migrate) it works fine as expected.
Unfortunately when I try to revert the migration ($ bundle exec rake db:rollback) it does not work and raises an ActiveRecord::IrreversibleMigration exception
== FixIndexes: reverting =====================================================
rake aborted!
An error has occurred, all later migrations canceled:
ActiveRecord::IrreversibleMigration/usr/local/rvm/gems/ruby-1.9.3-p392/gems/activerecord-3.2.14/lib/active_record/migration/command_recorder.rb:42:in `block in inverse'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/activerecord-3.2.14/lib/active_record/migration/command_recorder.rb:40:in `map'
My questions are:
Why this is an irreversible migration? It just adding and
removing some indexes, not data.
It will fix the problem if I use def self.up and def self.down
instead of def change?
How can I revert now this changes without adding and removing the
indexes manually from MySQL?
Only few commands are reversible(without manual commands) in Rails. And they are
add_column
add_index
add_timestamps
create_table
create_join_table
remove_timestamps
rename_column
rename_index
rename_table
Refer here
Your migration contains remove_index which is not supported by CommandRecorder for Rollback.
If you check this Documentation
Some transformations are destructive in a manner that cannot be
reversed. Migrations of that kind should raise an
ActiveRecord::IrreversibleMigration exception in their down method.
Which is the case in yours and obviously results in ActiveRecord::IrreversibleMigration exception.
Long Story short: remove_index cannot be reversed without manual commands.
So I accidentally named a column in my Item model "type" and I wrote a migration to rename it.
class RenameTypeToTagged < ActiveRecord::Migration
def up
rename_column :items, :type, :tagged
end
def down
end
end
When I restart the server, and rake db:migrate it still spits back "Can't mass-assign protected attributes: type". I've renamed the attr_accesible in the Item model by hand but it doesn't seem to resolve. any ideas? thanks
i don't know if it can help, but you have the possibility to rollback to a previous version of the migration. you can after redo a new migration file with the right field name.
to rollback to a previous migration, if your migration file with the error is named :
20130629033812_create_items.rb
you just have to type
rake db:migrate:down VERSION=20130629033812