Scott's Recipes Logo

Deleting Arbitrary Tables and Migrations from a Rails Application

NOTE: The better answer than using this is to use:

rake db:migrate:down VERSION=20100905201547

which I think should do what my rake task does. I left this post intact, however, because I only found this StackOverflow Post AFTER I wrote it. Sigh. If nothing else, the explanation of how migrations work may help someone.

I do a metric ton of what is called “Greenfield Development” as a solo developer. These two things are what led me to write a Rake task which executes like this:

be rake drop_table_and_migration:go[weathers,20220810081751]

Here’s what that looks like when it runs:

be rake drop_table_and_migration:go[weathers,20220810081751]
Success -- executed query: DROP TABLE weathers
Success -- executed query: DELETE FROM schema_migrations WHERE version = '20220810081751'
Table: weathers dropped and Migration file: 20220810081751 deleted from schema_migrations
You can now modify your schema and re-run your migrations with

bundle exec rake db:migrate

What that does is execute a Rake task which drops a table and deletes a migration entry from the schema_migrations registry.

The way that Rails migrations work is:

  1. A file named with a timestamp and a migration name is generated.
  2. The programmer modifies that file and adds the body of the migration.
  3. The db:migrate command is executed and the database is changed.
  4. The timestamp of the migration file is inserted into the table schema_migrations

Now there are commands to rollback a migration but they don’t, to my knowledge, allow you to cleanly do an arbitrary timestamp.

Now the reason I referenced both Greenfield Development and working as a solo developer is that this tool isn’t really designed to work in this context. In Greenfield Development, you tend to:

  1. Constantly develop table structures, try them out
  2. Realize a field is missing
  3. Try and rollback. Get it wrong and revert to raw SQL queries in Postgres.
  4. Modify your migration and then lather / rinse / repeat as needed.

Now you can do it with a command line like the one above.

Here’s the source code. Just add it into your Rails app as a Rake task and enjoy

namespace :drop_table_and_migration do
  # be rake drop_table_and_migration:go[weathers,20220810081751]
  task :go, [:table, :migration]  => :environment do |task, args|
    table_name = args[:table]
    migration_name = args[:migration]
    
    queries = []
    queries << "DROP TABLE #{table_name}"
    queries << "DELETE FROM schema_migrations WHERE version = '#{migration_name}'"
    queries.each do |query|
      begin
        ActiveRecord::Base.connection.execute(query)
      rescue Exception => e
        puts "Hit exception so exiting for safety: #{e.inspect}"
        exit
      end
      puts "Success -- executed query: #{query}"
    end
    puts "Table: #{table_name} dropped and Migration file: #{migration_name} deleted from schema_migrations"
    puts "You can now modify your schema and re-run your migrations with \n\nbundle exec rake db:migrate"
  end
end