Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I run a migration without starting a transaction in Rails?

Tags:

I'm running some bizarre Postgres migration code from OpenCongress and I'm getting this error:

RuntimeError: ERROR     C25001  MVACUUM cannot run inside a transaction block Fxact.c  L2649   RPreventTransactionChain: VACUUM FULL ANALYZE; 

So I'd like to try running it without getting wrapped by a transaction.

like image 293
agentofuser Avatar asked Sep 08 '09 18:09

agentofuser


People also ask

Are rails migrations run in a transaction?

On databases that support transactions with statements that change the schema, migrations are wrapped in a transaction. If the database does not support this then when a migration fails the parts of it that succeeded will not be rolled back. You will have to rollback the changes that were made by hand.

How do I run a specific migration in rails?

To run a specific migration up or down, use db:migrate:up or db:migrate:down . The version number in the above commands is the numeric prefix in the migration's filename. For example, to migrate to the migration 20160515085959_add_name_to_users. rb , you would use 20160515085959 as the version number.

How does migration work in rails?

A Rails migration is a tool for changing an application's database schema. Instead of managing SQL scripts, you define database changes in a domain-specific language (DSL). The code is database-independent, so you can easily move your app to a new platform.


2 Answers

There's now a method disable_ddl_transaction! that allows this, e.g.:

class AddIndexesToTablesBasedOnUsage < ActiveRecord::Migration   disable_ddl_transaction!   def up     execute %{       CREATE INDEX CONCURRENTLY index_reservations_subscription_id ON reservations (subscription_id);     }   end   def down     execute %{DROP INDEX index_reservations_subscription_id}   end end 
like image 87
davetron5000 Avatar answered Sep 26 '22 17:09

davetron5000


ActiveRecord::Migration has the following private method that gets called when running migrations:

def ddl_transaction(&block)   if Base.connection.supports_ddl_transactions?     Base.transaction { block.call }   else     block.call   end end 

As you can see this will wrap the migration in a transaction if the connection supports it.

In ActiveRecord::ConnectionAdapters::PostgreSQLAdapter you have:

def supports_ddl_transactions?   true end 

SQLite version 2.0 and beyond also support migration transactions. In ActiveRecord::ConnectionAdapters::SQLiteAdapter you have:

def supports_ddl_transactions?   sqlite_version >= '2.0.0' end 

So then, to skip transactions, you need to somehow circumvent this. Something like this might work, though I haven't tested it:

class ActiveRecord::Migration   class << self     def no_transaction       @no_transaction = true     end      def no_transaction?       @no_transaction == true     end   end    private      def ddl_transaction(&block)       if Base.connection.supports_ddl_transactions? && !self.class.no_transaction?         Base.transaction { block.call }       else         block.call       end     end end 

You could then set up your migration as follows:

class SomeMigration < ActiveRecord::Migration   no_transaction    def self.up     # Do something   end    def self.down     # Do something   end end 
like image 20
Peter Wagenet Avatar answered Sep 26 '22 17:09

Peter Wagenet