Search code examples
ruby-on-railsruby-on-rails-5

Rails: Renaming column in migration fails


I have a simple migration which fails: The database is not affected, it still contains the old column name after migrating.

class RenameTypeToLicenseTypeInLicenses < ActiveRecord::Migration[5.1]
  def change
    rename_column :licenses, :type, :license_type
  end
end

Renaming type to license_type is necessary because of this error:

The single-table inheritance mechanism failed to locate the subclass: 'creative commons'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite License.inheritance_column to use another column for that information.

This is the output of rails db:migrate

igrating to RenameTypeToLicenseTypeInLicenses (20210422110849)
   (0.2ms)  BEGIN
== 20210422110849 RenameTypeToLicenseTypeInLicenses: migrating ================
== 20210422110849 RenameTypeToLicenseTypeInLicenses: migrated (0.0000s) =======

  SQL (0.9ms)  INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version"  [["version", "20210422110849"]]
   (1.4ms)  COMMIT
  ActiveRecord::InternalMetadata Load (0.5ms)  SELECT  "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2  [["key", "environment"], ["LIMIT", 1]]
   (0.3ms)  BEGIN
   (0.3ms)  COMMIT
   (0.4ms)  SELECT pg_advisory_unlock(2195010657070977375)
   (0.6ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC

How can I rename the type column?

edit: As there's only little data in the table, I tried another approach:

remove_column :licenses, :type, :string
add_column :licenses, :license_type, :string

It doesn't work either. Output is

[NAME COLLISION] `type` is a reserved key in LicenseResource.
   (0.7ms)  SELECT pg_try_advisory_lock(2195010657070977375);
   (1.3ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Migrating to ChangeTypeToLicenseTypeFromLicenses (20210422122638)
   (0.5ms)  BEGIN
== 20210422122638 ChangeTypeToLicenseTypeFromLicenses: migrating ==============
== 20210422122638 ChangeTypeToLicenseTypeFromLicenses: migrated (0.0000s) =====

  SQL (1.0ms)  INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version"  [["version", "20210422122638"]]
   (4.6ms)  COMMIT
  ActiveRecord::InternalMetadata Load (0.4ms)  SELECT  "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2  [["key", "environment"], ["LIMIT", 1]]
   (0.4ms)  BEGIN
   (0.4ms)  COMMIT
   (0.6ms)  SELECT pg_advisory_unlock(2195010657070977375)
   (0.9ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC

Solution

  • Yeah, I wonder if ActiveRecord's migration code might be trying its best to keep type intact, on the assumption that it's currently being used for single table inheritance; it's oblivious to the fact you want to change it because it currently isn't 😊

    There may be an idiomatic Rails way of telling the migration that what you're doing is fine, but to be honest I'd be tempted to drop down to the SQL level, thus bypassing Rails' erroneous protection:

    class RenameTypeToLicenseTypeInLicenses < ActiveRecord::Migration[5.1]
      def up
        execute <<~SQL
          ALTER TABLE licenses RENAME COLUMN type TO license_type;
        SQL
      end
    
      def down
        execute <<~SQL
          ALTER TABLE licenses RENAME COLUMN license_type TO type;
        SQL
      end
    end
    

    As you're not using migration commands, you have to provide separate up/down code for both directions of the migration.