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

Fixed #20581 -- Added support for deferrable unique constraints. #10338

Merged
merged 1 commit into from
Apr 30, 2020

Conversation

LilyFoote
Copy link
Contributor

@LilyFoote LilyFoote commented Aug 27, 2018

This builds upon #10271, extending the UniqueConstraint class to support deferring constraint checks.
https://code.djangoproject.com/ticket/20581

django/db/backends/postgresql/schema.py Outdated Show resolved Hide resolved
django/db/models/constraints.py Outdated Show resolved Hide resolved
@LilyFoote LilyFoote force-pushed the ticket_20581 branch 7 times, most recently from e63e6cd to d8786b7 Compare September 2, 2018 11:03
@LilyFoote LilyFoote force-pushed the ticket_20581 branch 2 times, most recently from 52b99c3 to f514cca Compare September 19, 2018 10:25
@LilyFoote LilyFoote force-pushed the ticket_20581 branch 2 times, most recently from 8e41e20 to 5eca9f8 Compare November 13, 2018 23:25
@auvipy
Copy link
Contributor

auvipy commented Nov 15, 2018

is it ready for review?

@LilyFoote
Copy link
Contributor Author

@auvipy Yes.

@LilyFoote LilyFoote force-pushed the ticket_20581 branch 2 times, most recently from 1be10ff to af9caf3 Compare February 24, 2019 14:51
@LilyFoote
Copy link
Contributor Author

@auvipy I've synced this with master. I've also fixed a bug in set_constraints, clarified the interaction between condition and defer with a ValueError, added Oracle support and added a first draft of documentation. I'd appreciate it if you could review again.

@auvipy
Copy link
Contributor

auvipy commented Feb 24, 2019

OK I will

@LilyFoote
Copy link
Contributor Author

I haven't tested the Oracle support, because I don't have Oracle configured locally. I think it should work, from my reading of the docs, but it's possible something isn't quite right.

Copy link
Contributor

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

looks great overall.

django/db/models/constraints.py Outdated Show resolved Hide resolved
Copy link
Contributor

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

looks great to merge.

@LilyFoote
Copy link
Contributor Author

@carltongibson @felixxm I'd like to land this today if possible. I think the main thing it needs is release notes. Is there anything else I need to do?

@felixxm
Copy link
Member

felixxm commented Apr 13, 2019

IMO it should work on Oracle, can you enable supports_deferrable_unique_constraints? Do you know how it looks like on MySQL?

@LilyFoote LilyFoote force-pushed the ticket_20581 branch 2 times, most recently from 9b7f86a to 017ad48 Compare April 13, 2019 09:24
@LilyFoote
Copy link
Contributor Author

LilyFoote commented Apr 13, 2019

As far as I can tell, MySQL and MariaDB do not support deferrable constraints. I've added a small release note and enabled this for Oracle.

tests/constraints/tests.py Outdated Show resolved Hide resolved
django/db/backends/base/schema.py Outdated Show resolved Hide resolved
Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

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

@Ian-Foote Thanks 👍 We have some failures on Oracle (with proposed changes):

  • test_add_deferrable_unique_constraint (migrations.test_operations.OperationTests),
  • test_create_model_with_deferrable_unique_constraint (migrations.test_operations.OperationTests),
  • test_remove_deferrable_unique_constraint (migrations.test_operations.OperationTests), and
    raise
django.db.utils.IntegrityError: ORA-02091: transaction rolled back
ORA-00001: unique constraint (DJANGOTEST.DEFERRABLE_PINK_CONSTRAINT) violated

Also I think we should add a system checks for databases that don't support defferable, like we added for condition (ticket-31351).

docs/ref/models/constraints.txt Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
tests/migrations/test_operations.py Outdated Show resolved Hide resolved
django/db/backends/base/schema.py Outdated Show resolved Hide resolved
django/db/backends/base/schema.py Outdated Show resolved Hide resolved
@felixxm
Copy link
Member

felixxm commented Apr 29, 2020

@Ian-Foote Thanks for updates 👍 I pushed small edits and:

  • added deferable to the UniqueConstraint.__repr__() (with tests),
  • added deferable to the UniqueConstraint.__eq__() (with tests),
  • avoided creating/removing deferable unique constraints if not supported.

I'm not convinced that we need Deferrable enum, it's an advance flag (like index_type in ExclusionConstraint) and IMO we can handle it in the same way, as a string. If you agree I can push required changes.

I'm still fighting with migrations.test_operations 👎

Please don't use force-push if you will do any changes, it's harder to review such changes.

@ngnpope
Copy link
Member

ngnpope commented Apr 29, 2020

+1 on enum removal. For the simple case of two single-word strings we end up with an extra object to import...

@meshy
Copy link
Sponsor Contributor

meshy commented Apr 29, 2020

I think it's better to keep the enum. It avoids the possibility of a typo. There is precident with things like passing on_delete=CASCADE to ForeignKeys.

Edit: or, if not an enum, at least an importable constant would be good.

@LilyFoote
Copy link
Contributor Author

I originally used importable strings instead of the Enum and changed it in response to #10338 (comment). See also this discussion: #10338 (comment).

At this point, I prefer the Enum over the other two options:

  • Importable string constants
  • Two boolean flags

@ngnpope
Copy link
Member

ngnpope commented Apr 29, 2020

@meshy django.db.models.deletion.CASCADE & co. are not enums, but functions that actually define the logic for how to handle the deletion behaviour. Note that they are executed application-side and not enforced on the database - see #8661 for further work in that area - so they are not strings that are passed down to the database.

It avoids the possibility of a typo.

That can be easily be handled by raising a ValueError if a valid string is not provided in the same way that is done for Deferrable currently:

-        if deferrable is not None and not isinstance(deferrable, Deferrable):
+        if deferrable is not None and deferrable not in {'deferred', 'immediate'}:

Removing the enum avoids having to import it in various places, including an inline import in the _deferrable_constraint_sql() method.

Edit: or, if not an enum, at least an importable constant would be good.

That would be worse than an enum - you'd now need two constants and it wouldn't prevent use of an arbitrary string anyway as we would still have to validate the values as there would be no type checking.

Whether an enum or multiple constants, this also requires additional documentation of these objects/constants rather than just stating the two accepted string values for the deferrable argument in the documentation.

@ngnpope
Copy link
Member

ngnpope commented Apr 29, 2020

@Ian-Foote Sorry - we posted simultaneously.

At this point, I prefer the Enum over the other two options:

As pointed out by @felixxm, there is the third option is to just use the string values 'deferred' and 'immediate' and validate them in the constructor. Constants for the string values doesn't provide any guarantees that they will be used appropriately - you only need to look at f1664a2, e7033e0, and 1e0dcd6 - you still need to check the value is valid.

Don't get me wrong - enums do have benefits, I'm just not sure that it is worth it in this instance when there are only two possible values, and when that means importing Deferrable all over the place.

Anyway, that's just my 2¢ and I'll leave it at that. I see that @charettes was for the enum and am happy to go with his wisdom.

@LilyFoote
Copy link
Contributor Author

Good point that I misunderstood @felixxm's suggestion. I think I still prefer the Enum slightly, but I don't feel as strongly as compared to the other two options.

@charettes
Copy link
Member

charettes commented Apr 29, 2020

I don't have a strong feeling either way, using an Enum felt more natural to me. I'd say that ExclusionConstraint.index_type is a bit different to me because PostgreSQL allows a ton different index type to used and it's even possible to define your own index type in extensions. It's not possible to do the same thing with constraint defer-ability and if we ever implement ForeignKeyConstraint we'll be able to reuse this constant.

@LilyFoote
Copy link
Contributor Author

I already reuse Deferrable in ExclusionConstraint: #12704

@felixxm
Copy link
Member

felixxm commented Apr 29, 2020

OK, let's stay with Enum, I will continue tomorrow.

@felixxm felixxm changed the title Fixed #20581 -- Add support for deferrable unique constraints Fixed #20581 -- Added support for deferrable unique constraints. Apr 30, 2020
Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

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

@Ian-Foote Many thanks for your hard work and patience 🚀 It took several DjangoCon's 😉

I pushed final edits and fixed tests on Oracle.

@felixxm
Copy link
Member

felixxm commented Apr 30, 2020

oragis failure is not related with this patch, it's caused by recent changes in Jenkins configuration.

@felixxm felixxm merged commit c226c6c into django:master Apr 30, 2020
@LilyFoote LilyFoote deleted the ticket_20581 branch April 30, 2020 10:34
@charettes
Copy link
Member

Congrats @Ian-Foote and thank you for your continued efforts 🙇 🎉 !

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

Successfully merging this pull request may close these issues.

9 participants