Skip to content

Comments

Add basic support for check constraints to database migrations#31323

Merged
jeremy merged 1 commit intorails:masterfrom
fatkodima:check_constraints
Jun 3, 2020
Merged

Add basic support for check constraints to database migrations#31323
jeremy merged 1 commit intorails:masterfrom
fatkodima:check_constraints

Conversation

@fatkodima
Copy link
Member

@fatkodima fatkodima commented Dec 3, 2017

This patch adds basic support for adding and removing check constraints using the migration DSL:

create_table :distributors do |t|
  t.string :zipcode
  t.check_constraint "zipchk", "char_length(zipcode) = 5"   
end

remove_check_constraint :distributors, "zipchk"

Check constraints are dumped inside create_table sections to schema.rb.

add_check_constraint and remove_check_constraint are both reversible.

Considerations

  • Maybe remove the _constraint part from new methods names? Seems a little redundant
  • Add additional options, such as NO INHERIT(PostgreSQL)?

Related #23083.

@rails-bot
Copy link

Thanks for the pull request, and welcome! The Rails team is excited to review your changes, and you should hear from @schneems (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

This repository is being automatically checked for code quality issues using Code Climate. You can see results for this analysis in the PR status below. Newly introduced issues should be fixed before a Pull Request is considered ready to review.

Please see the contribution instructions for more information.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why was every line in this hash changed when you only added a single line?

Copy link
Member

Choose a reason for hiding this comment

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

@sgrif because it's very clearly [already] aligned to the longest key?

(though it does seem to have ended up one space further over than it should be.)

Copy link
Contributor

@sgrif sgrif Dec 4, 2017

Choose a reason for hiding this comment

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

Sorry, I should have been more clear on my intent:

Do you think we could drop the visual alignment change here so we aren't changing every line in this hash? I think having the one line that is not aligned the same is fine.

Copy link
Contributor

@sgrif sgrif left a comment

Choose a reason for hiding this comment

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

I don't think the tests here are sufficient. There are no tests which check that the constraint has actually been added to the table (which can only really be tested by attempting an insertion that should fail).

In particular, I don't think you've handled SQLite correctly, where check constraints are supported, but can only be added inline with the column definition. Unless I'm missing something, there's no way to add a check constraint as part of a column definition in this PR at all.

@lostapathy
Copy link
Contributor

+1 to @sgrif's thoughts on sqlite. I also recently discovered that add_reference doesn't create foreign keys on sqlite, even though sqlite could support it. I'm working on a PR for that, and it'd great to not see sqlite fall behind farther.

@matthewd
Copy link
Member

matthewd commented Dec 4, 2017

Unless I'm missing something, there's no way to add a check constraint as part of a column definition in this PR at all.

That sounds more like an additional feature than a flaw in this PR, per se.

(i.e., if the PR were implementing column-level constraints and had omitted SQLite, that would be more of a problem; whereas I think only implementing table-level constraints is a perfectly reasonable self-contained feature, particularly as someone's first substantial contribution.)

@sgrif
Copy link
Contributor

sgrif commented Dec 4, 2017

I'm fine with omitting SQLite support entirely for this feature to start, but I still think we need tests which actually exercise the added constraint.

@lostapathy
Copy link
Contributor

@matthewd you're right, that makes sense. This API makes sense in general, just not for sqlite. Totally fair to skip it. Much like how add_foreign_key can't work on sqlite, but add_reference should be doable.

Would it be possible/tolerable to emit some kind of warning if a constraint (or foreign key) migration is treated as a no-op sqlite3? I spent a ton of time tracing that behavior out and I'm not sure what we do now is intuitive. The sqlite docs say they support foreign keys and foreign key migrations are "successful", so it one does not expect to end up without a foreign key in their sqlite db.

@fatkodima fatkodima force-pushed the check_constraints branch 2 times, most recently from 4845759 to ab123b4 Compare December 5, 2017 21:07
@fatkodima
Copy link
Member Author

Updated PR. Added missing test (suggested by @sgrif ) and support for sqlite.

@pkoch
Copy link

pkoch commented Sep 5, 2018

What's missing to move forward on this branch atm?

@NikolayS
Copy link

NikolayS commented Apr 23, 2019

This is a good feature. It's 2019 already, and SQL-92 standard had CHECK. Good time to start supporting it, both for MySQL and Rails.

@fatkodima
Copy link
Member Author

If core team will accept this PR, I will update it as needed.
Btw, right now I think it is a bad idea to support constraints, because currently migrations are not far from being bloated and there will be people who wants to support other not so popular features like triggers and other db stuff.

@NikolayS
Copy link

NikolayS commented Apr 23, 2019

Constraints enforcement is a database system's job.

In general, only DBMS is able to reliably enforce constraints in 100% of cases. If you implement checks in your app logic, there are significant chances that after one year or so they will be silently broken, because some human accessed DB directly, changing data, or because you've launched, say, API using a different language/framework, or because you've used some DB migrations bypassing your abstraction layer which tried to enforce constraints.

CHECK is a very good thing, underestimated due to lack of its support in MySQL (until 8.0.16, not yet released) and lack of its support in frameworks. This is a classic "chicken vs egg" problem: without support in frameworks, people won't start using it, saying that it's not so popular (but it is a very popular for decades already; not among Ruby developers).

@fatkodima
Copy link
Member Author

@matthewd Wdyt about moving this forward?

@rails-bot
Copy link

rails-bot bot commented Mar 3, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Thank you for your contributions.

@rails-bot rails-bot bot added the stale label Mar 3, 2020
@pkoch
Copy link

pkoch commented Mar 3, 2020

@matthewd @sgrif I'd love to not let this die. What would it take to get a ship it?

@rails-bot rails-bot bot removed the stale label Mar 3, 2020
Copy link
Member

@jeremy jeremy left a comment

Choose a reason for hiding this comment

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

🙌🏼

Method naming

Maybe remove the _constraint part from new methods names? Seems a little redundant

Comparing,

create_table :distributors do |t|
  t.string :zipcode
  t.check_constraint "zipchk", "char_length(zipcode) = 5"   
end

remove_check_constraint :distributors, "zipchk"
create_table :distributors do |t|
  t.string :zipcode
  t.check "zipchk", "char_length(zipcode) = 5"   
end

remove_check :distributors, "zipchk"

I prefer the clarity of seeing constraint mentioned. If it were a constraint on a column definition, check: "expression" would be nicer.

Naming constraints
Having to name constraints is a hassle, too. Can we name them automatically, like we do with foreign keys and indexes? For example, we could transform the check expression into name like chk_rails_#{hashed_identifier}.

@fatkodima fatkodima force-pushed the check_constraints branch from 14b1e36 to c19da90 Compare March 4, 2020 19:08
@fatkodima
Copy link
Member Author

  1. Updated this with current master branch.
  2. fixed @jeremy suggestions (including optional constraint name feature)
  3. added missing implementation for mysql

@rails-bot
Copy link

rails-bot bot commented Jun 2, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Thank you for your contributions.

@rails-bot rails-bot bot added the stale label Jun 2, 2020
@jeremy jeremy added this to the 6.1.0 milestone Jun 2, 2020
@rails-bot rails-bot bot removed the stale label Jun 2, 2020
@jeremy jeremy self-assigned this Jun 2, 2020
Copy link
Member

@jeremy jeremy left a comment

Choose a reason for hiding this comment

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

Ready to merge pending rebase. Thanks for your work on this @fatkodima.

@fatkodima fatkodima force-pushed the check_constraints branch 5 times, most recently from b43feb3 to ac01c4d Compare June 3, 2020 00:55
@fatkodima fatkodima force-pushed the check_constraints branch from ac01c4d to c3ee7da Compare June 3, 2020 01:11
@fatkodima
Copy link
Member Author

@jeremy Rebased.

@NikolayS
Copy link

NikolayS commented Jun 3, 2020

Wow. Great news 👍

agrobbin added a commit to agrobbin/rails that referenced this pull request Sep 7, 2020
Active Record already supports adding `NOT VALID` foreign key constraints in PostgreSQL, but back in rails#31323 when check constraint support was initially added, the ability to add a check constraint and validate it separately was not included. This adds that functionality!
drwl added a commit to drwl/annotaterb that referenced this pull request Apr 12, 2024
Supports annotating model check constraints, credit goes to folks who
worked on ctran/annotate_models#868.

Adds new option `show_check_constraints` that defaults to `false`. When
enabled, it will add check constraints annotations
rails/rails#31323 to the model annotations.

It can be enabled also through the command line with options `-c` or
`--show-check-constraints`.

Resolves #104
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.

10 participants