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

change_column_null should raise if a non-boolean 3rd argument is provided #45229

Merged
merged 1 commit into from
Jun 13, 2022

Conversation

ghiculescu
Copy link
Member

Currently if you provide a non-boolean argument, change_column_null will treat it as truthy and make your column nullable. This might not be what you want. For example, I've noticed this happen a few times, where someone assumes that change_column_null and change_column_default have the same signature:

change_column_default(:posts, :state, from: nil, to: "draft")
change_column_null(:posts, :state, from: true, to: false)

Reading the migration you would expect that the default is now "draft" and the column doesn't accept nulls. But actually, the "null" argument is { from: true, to: false } which is truthy, so now the column accepts nulls. If you aren't paying close attention to the migration output you might miss this.

I think we can protect users here by having change_column_null require the 3rd argument to be true or false and raising an error otherwise. This PR implements that, for migrations created on Rails 7.1 onward.

@ghiculescu
Copy link
Member Author

@fatkodima thanks for the great review ❤️

Copy link
Member

@byroot byroot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems sensible, I'm 👍 . It just need a rebase. Let me know when it's down and I'll happily merge.

…ovided

Currently if you provide a non-boolean argument, `change_column_null` will treat it as truthy and make your column nullable. This might not be what you want. For example, I've noticed this happen a few times, where someone assumes that `change_column_null` and `change_column_default` work the same:

```ruby
change_column_default(:posts, :state, from: nil, to: "draft")
change_column_null(:posts, :state, from: true, to: false)
```

Reading the migration you would expect that the default is now "draft" and the column doesn't accept nulls. But actually, the "null" argument is `{ from: true, to: false }` which is truthy, so now the column accepts nulls. If you aren't paying close attention to the migration output you might miss this.

I think we can protect users here by having `change_column_null` require the 3rd argument to be `true` or `false` and raising an error otherwise. This PR implements that, for migrations created on Rails 7.1 onward.
@simi
Copy link
Contributor

simi commented Jun 13, 2022

LGTM

Btw. what about other methods handling DB NULL like add_column(..., null: 'random_string')? I have quickly tested add_column and it behaves the same (any truthy value => truth).

@ghiculescu
Copy link
Member Author

👍 that's a good call. We should do that too.

I can open a followup PR for that, unless @byroot (or anyone else) feels strongly about it being in the same PR.

@byroot
Copy link
Member

byroot commented Jun 13, 2022

like add_column(..., null: 'random_string')

In this case it's much more obvious that a boolean is expected.

The reason I'm 👍 on this particular PR is that it's indeed very easy to make that mistake, but it doesn't mean that we should check the type of every params everywhere.

@byroot byroot merged commit 2026d5b into rails:main Jun 13, 2022
@ghiculescu ghiculescu deleted the change-column-null-ux branch June 13, 2022 19:01
@simi
Copy link
Contributor

simi commented Jun 13, 2022

I can open a followup PR for that, unless @byroot (or anyone else) feels strongly about it being in the same PR.

Sorry for confusion, my intention wasn't to extend scope of this PR. I was just wondering if this is start of new pattern -> checking migration params. It seems random now. Something is checked, something not now. 🤷‍♂️

🤔 Also looking at the code, currently it is needed to do validation in each adapter on its own. That means adapters maintained
outside of AR will not benefit from this change. Currently it seems impossible to simply do this kind of check at one place. 😢

@byroot for the future, would it be welcomed to somehow wrap this into one shared entry for all adapters? I can try to prepare some initial PR to discuss. That way it would be much easier to bring similar changes.

@byroot
Copy link
Member

byroot commented Jun 13, 2022

If you find a way to avoid that duplication and to support third-party adapter, then yes it's welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants