Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove_column should not be included in BulkChangeTable cop #110

Closed
connorshea opened this issue Aug 18, 2019 · 8 comments
Closed

remove_column should not be included in BulkChangeTable cop #110

connorshea opened this issue Aug 18, 2019 · 8 comments

Comments

@connorshea
Copy link

Rubocop warns about multiple uses of remove_column in the same migration, saying it should be changed to use a change_table, bulk: true. However, change_table has no equivalent means of removing a column in a way which can be reversed. So when you try to fix the warning, a new warning for a non-reversible migration shows up.


Expected behavior

I would expect that multiple uses of remove_column would not cause a warning from Rails/BulkChangeTable, because remove_column can't be converted to change_table in a way that's reversible.

Actual behavior

db/migrate/20190817225925_replace_before_after_values_with_differences_column.rb:4:5: C: Rails/BulkChangeTable: You can use change_table :game_purchase_events, bulk: true to combine alter queries.
    remove_column :game_purchase_events, :before_value, :text
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Or, if you change it to use change_table, bulk: true:

db/migrate/20190817225925_replace_before_after_values_with_differences_column.rb:6:7: C: Rails/ReversibleMigration: change_table(with remove) is not reversible.
      t.remove :after_value, :text
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Steps to reproduce the problem

  1. Create a migration like so:
class AddAndRemoveColumnsToTable < ActiveRecord::Migration[6.0]
  def change
    remove_column :table_name, :before_value, :text
    remove_column :table_name, :after_value, :text
    add_column :table_name, :differences, :jsonb
  end
end
  1. Enable the Rails/BulkChangeTable and Rails/ReversibleMigration cops
  2. Run bundle exec rubocop, see that there are violations for Rails/BulkChangeTable.
  3. Change your migration to look like this instead:
class AddAndRemoveColumnsToTable < ActiveRecord::Migration[6.0]
  def change
    change_table :table_name, bulk: true do |t|
      t.remove :before_value
      t.remove :after_value
      t.jsonb :differences
    end
  end
end
  1. Run Rubocop again
  2. Note that now there's a lint error for Rails/ReversibleMigration (see the docs), t.remove is an alias of remove_columns and it doesn't seem like there's any way to specify a column type. So it's not possible (as far as I can tell) to reversibly use change_table, bulk: true when removing columns.

RuboCop version

0.74.0
@wata727
Copy link
Contributor

wata727 commented Aug 19, 2019

Hi @connorshea, I'm an author of this cop.

If you focus only on suppressing warnings, there is a way to define up and down methods respectively. This is a completely reversible migration.

class AddAndRemoveColumnsToTable < ActiveRecord::Migration[6.0]
  def up
    change_table :table_name, bulk: true do |t|
      t.remove :before_value
      t.remove :after_value
      t.jsonb :differences
    end
  end

  def down
    change_table :table_name, bulk: true do |t|
      t.text :before_value
      t.text :after_value
      t.remove :differences
    end
  end
end

But if you feel this way is redundant, I think you should not follow the Rails/BulkChangeTable cop. The bulk is an option and you should use the appropriate option for your situation.

Initially, I added this cop for telling us about how to use grouping alter statements, but I understand the opinion that it's a bit too assertive.

@connorshea
Copy link
Author

Fair enough, thanks for clarifying 👍

@eugeneius
Copy link
Contributor

Note that remove_columns / t.remove will be reversible in Rails 6.1 via rails/rails#36589.

@Tao-Galasse
Copy link

Hi @wata727 ! Now that Rails 6.1 released, I feel like this issue could be re-evaluated. Should a line like t.remove :my_string, type: :string in a bulk migration raise a lint error ?

koic added a commit to koic/rubocop-rails that referenced this issue Sep 17, 2021
Follow up to rubocop#110 (comment).

This PR fixes a false positive for `Rails/ReversibleMigration`
when using `t.remove` with `type` option in Rails 6.1.
Rosa-Fox added a commit to alphagov/collections-publisher that referenced this issue Oct 19, 2021
The [original link fields]( https://github.com/alphagov/govuk-coronavirus-content/blob/12bacb98cdfa9a96a8f6f344143be0be1361babd/content/coronavirus_landing_page.yml#L14-L15) are used to determine where the action link text in the header should wrap when the landing page is being viewed in mobile view.

For example, at the moment the fields contain "Find out how to stay safe and help" and "prevent the spread". On a desktop this
is displayed as one line "Find out how to stay safe and help prevent the spread", but on a mobile, it wraps in the expected pla
ce.
i.e. the use sees:

 "Find out how to stay safe and help"
 "prevent the spread"

This commit provides two header text fields: `header_link_pre_wrap_text` and `header_link_post_wrap_text`.

In the migration, I initially wanted to use (header_link_post_wrap_text omitted from the following examples to save space):

```
  def change
    rename_column :coronavirus_pages, :header_link_text, :header_link_pre_wrap_text
    change_column :coronavirus_pages, :header_link_pre_wrap_text, :string
  end
```

However the linter threw a ‘Rails/BulkChangeTable: You can use change_table :coronavirus_pages, bulk: true to combine alter que
ries.’ error… hence using `bulk: true`.

 ```
  def change
    change_table :coronavirus_pages, bulk: true do |t|
      t.rename :header_link_text, :header_link_pre_wrap_text
      t.string :header_link_pre_wrap_text
    end
  end
 ```

This did not remove the header_link_text and added the header_link_pre_wrap_text. I decided to remove the colu
mn instead:

```
  def change
    change_table :coronavirus_pages, bulk: true do |t|
      t.remove :header_link_text
      t.string :header_link_pre_wrap_text
    end
  end
```

However the linter threw a `Rails/ReversibleMigration` error.

I therefore used `up` and `down` methods to make the migration reversable, as [suggested by the author of the cop](https://gith
ub.com/rubocop/rubocop-rails/issues/110#issuecomment-522461464). Annoyingly `remove_column` is usually reversible with a `type` but
 the bulk [change_table syntax](http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/change_table) does not al
low a type to be specified.

Its a bit convoluted to appease the linter... it seems to have been raised as an issue to [Rubocop](https://github.com/rubocop/
rubocop-rails/issues/161) but oh well.
leenagupte pushed a commit to alphagov/collections-publisher that referenced this issue Oct 28, 2021
The [original link fields] are used to determine where the action link
text in the header should wrap when the landing page is being viewed in
mobile view.

For example, at the moment the fields contain "Find out how to stay safe
and help" and "prevent the spread". On a desktop this is displayed as
one line "Find out how to stay safe and help prevent the spread", but on
a mobile, it wraps in the expected place.
i.e. the use sees:

 "Find out how to stay safe and help"
 "prevent the spread"

This commit provides two header text fields: `header_link_pre_wrap_text`
and `header_link_post_wrap_text`.

In the migration, I initially wanted to use (header_link_post_wrap_text
omitted from the following examples to save space):

```
  def change
    rename_column :coronavirus_pages, :header_link_text, :header_link_pre_wrap_text
    change_column :coronavirus_pages, :header_link_pre_wrap_text, :string
  end
```

However the linter threw a:

```
Rails/BulkChangeTable: You can use change_table :coronavirus_pages, bulk: true to combine alter queries.
```

error… hence using `bulk: true`.

 ```
  def change
    change_table :coronavirus_pages, bulk: true do |t|
      t.rename :header_link_text, :header_link_pre_wrap_text
      t.string :header_link_pre_wrap_text
    end
  end
 ```

This did not remove the header_link_text and added the
header_link_pre_wrap_text. I decided to remove the column instead:

```
  def change
    change_table :coronavirus_pages, bulk: true do |t|
      t.remove :header_link_text
      t.string :header_link_pre_wrap_text
    end
  end
```

However the linter threw a `Rails/ReversibleMigration` error.

I therefore used `up` and `down` methods to make the migration
reversable, as [suggested by the author of the cop]. Annoyingly
`remove_column` is usually reversible with a `type` but the bulk
[change_table syntax] does not allow a type to be specified.

Its a bit convoluted to appease the linter... it seems to have been
raised as an issue to [Rubocop] but oh well.

[original link fields]: https://github.com/alphagov/govuk-coronavirus-content/blob/12bacb98cdfa9a96a8f6f344143be0be1361babd/content/coronavirus_landing_page.yml#L14-L15
[suggested by the author of the cop]: rubocop/rubocop-rails#110 (comment)
[change_table syntax]: http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/change_table
[Rubocop]: rubocop/rubocop-rails#161
leenagupte added a commit to alphagov/whitehall that referenced this issue Aug 31, 2022
leenagupte added a commit to alphagov/whitehall that referenced this issue Sep 13, 2022
@koic
Copy link
Member

koic commented Apr 4, 2023

@wata727 Can you sort out the current situation and work on this?

@wata727
Copy link
Contributor

wata727 commented Apr 4, 2023

I believe this issue is already ready to be closed. The remove_column is a bulk changeable transformation and should be included in the BulkChangeTable cop. As a result of the fix, the ReversibleMigration cop may add a new offense, but it's avoidable and it's not the responsibility of this cop.

@wata727
Copy link
Contributor

wata727 commented Apr 4, 2023

By the way, this list of combinable transformations seems a bit outdated. Perhaps we can add the list below:

  • change_default
  • change_null (PostgreSQL only)
  • blob
  • tinyblob (MySQL only)
  • mediumblob (MySQL only)
  • longblob (MySQL only)
  • tinytext (MySQL only)
  • mediumtext (MySQL only)
  • longtext (MySQL only)
  • unsigned_integer (MySQL only)
  • unsigned_bigint (MySQL only)
  • unsigned_float (MySQL only)
  • unsigned_decimal (MySQL only)
  • bigserial (PostgreSQL only)
  • bit (PostgreSQL only)
  • bit_varying (PostgreSQL only)
  • cidr (PostgreSQL only)
  • citext (PostgreSQL only)
  • daterange (PostgreSQL only)
  • hstore (PostgreSQL only)
  • inet (PostgreSQL only)
  • interval (PostgreSQL only)
  • int4range (PostgreSQL only)
  • int8range (PostgreSQL only)
  • jsonb (PostgreSQL only)
  • ltree (PostgreSQL only)
  • macaddr (PostgreSQL only)
  • money (PostgreSQL only)
  • numrange (PostgreSQL only)
  • oid (PostgreSQL only)
  • point (PostgreSQL only)
  • line (PostgreSQL only)
  • lseg (PostgreSQL only)
  • box (PostgreSQL only)
  • path (PostgreSQL only)
  • polygon (PostgreSQL only)
  • circle (PostgreSQL only)
  • serial (PostgreSQL only)
  • tsrange (PostgreSQL only)
  • tstzrange (PostgreSQL only)
  • tsvector (PostgreSQL only)
  • uuid (PostgreSQL only)
  • xml (PostgreSQL only)
  • timestamptz (PostgreSQL only)
  • enum (PostgreSQL only)

References

@koic
Copy link
Member

koic commented Apr 4, 2023

@wata727 Superb! I'll close this issue. OTOH, If you'd like, please open a PR as the results of your investigation if necessary. Thank you!

@koic koic closed this as completed Apr 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants