aarona
aarona

Reputation: 37324

Rails migration bulk updates for change_column_default

I created a migration that does two default value updates. It looks something like this:

class ChangeTableNameDefaultValues < ActiveRecord::Migration[7.2]
  def change
    change_column_default(:table_name, :column_1, from: 'old_default_value', to: 'new_default_value')
    change_column_default(:table_name, :column_2, from: 'old_default_value', to: 'new_default_value')
  end
end

My linter suggested I wrap this with a change_table bulk: true statement so I tried it like this:

class ChangeTableNameDefaultValues < ActiveRecord::Migration[7.2]
  def change
    change_table :table_name, bulk: true do |t|
      # Notice here that I don't use the t variable being passed
      # to the block. I'm wondering if I need to call a method on t?
      change_column_default(:table_name, :column_1, from: 'old_default_value', to: 'new_default_value')
      change_column_default(:table_name, :column_2, from: 'old_default_value', to: 'new_default_value')
    end
  end
end

However, after running the migration, I got the following output:

Migrating to ChangeTableNameDefaultValues (20250221054551)
== 20250221054551 ChangeTableNameDefaultValues: migrating ======================
-- change_table(:table_name, {:bulk=>true})
-- change_column_default(:table_name, :column_1, {:from=>"old_default_value", :to=>"new_default_value"})
  TRANSACTION (0.0ms)  BEGIN
   (2.4ms)  ALTER TABLE "table_name" ALTER COLUMN "column_1" SET DEFAULT 'new_default_value'
   -> 0.0046s
-- change_column_default(:table_name, :column_2, {:from=>"old_default_value", :to=>"new_default_value"})
   (0.5ms)  ALTER TABLE "table_name" ALTER COLUMN "column_2" SET DEFAULT 'new_default_value'
   -> 0.0017s
   -> 0.0072s
== 20250221054551 ChangeTableNameDefaultValues: migrated (0.0072s) =============

So it didn't take advantage of the bulk table updating. I'm wondering if this is possible with changing default values of columns?

Upvotes: 0

Views: 39

Answers (1)

dbugger
dbugger

Reputation: 16464

The issue is that you need to use change_default instead of change_column_default.

change_default uses the context of the table in the change_table block, while change_column_default takes the table as an explicit argument and generates its own statements.

So something like...

  def change
    change_table :table_name, bulk: true do |t|
      # No table needed, already available in context
      t.change_default(:column_1, from: 'old_default_value', to: 'new_default_value')
      t.change_default(:column_2, from: 'old_default_value', to: 'new_default_value')
    end
  end

Note that this is NOT reversible.

The list of methods available in change_table are here.

Upvotes: 3

Related Questions