postgresqlruby-on-rails-4migrationintervalscolumn-types

How do I change my column type to interval in my Rails migration?


I”m using Rails 4.2.3 with a PostGre db. I’m trying to change a column type in my table to an “interval” type, so I tried this migration

class ChangeTimeInMsInMyObjectTimes < ActiveRecord::Migration
  def change
    change_column :my_object_times, :time_in_ms, :interval
  end
end

but upon running “rake db:migrate,” I get the below disappointing error …

== 20160530164019 ChangeTimeInMsInMyObjectTimes: migrating ========================
-- change_column(:my_object_times, :time_in_ms, :interval)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::DatatypeMismatch: ERROR:  column "time_in_ms" cannot be cast automatically to type interval
HINT:  You might need to specify "USING time_in_ms::interval".
: ALTER TABLE "my_object_times" ALTER COLUMN "time_in_ms" TYPE interval
/Users/davea/.rvm/gems/ruby-2.3.0@global/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:155:in `async_exec'
/Users/davea/.rvm/gems/ruby-2.3.0@global/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:155:in `block in execute'
/Users/davea/.rvm/gems/ruby-2.3.0@global/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/abstract_adapter.rb:472:in `block in log'
/Users/davea/.rvm/gems/ruby-2.3.0@global/gems/activesupport-4.2.5.1/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
/Users/davea/.rvm/gems/ruby-2.3.0@global/gems/activerecord-4.2.5.1/lib/active_record/connection_adapters/abstract_adapter.rb:466:in `log'

How do I change my migration to get this to work?


Solution

  • The problem is that Postgres doesn't know how to cast an integer (I assume that's what time_in_ms is right now.) to an interval. But you can tell it how with the USING keyword. It takes an expression that Postgres will use to convert all the old values. So you can use this in your migration:

    change_column :my_object_times, :time_in_ms,
                  "interval USING (time_in_ms || ' milliseconds')::interval"
    

    Note if you do this you should write separate up and down methods, because this way of using change_column isn't automatically reversible.

    Oh also: you can ignore the gist from @Boltz0r. As @mu says it is trying to solve a different problem.