From 719b7466203181066d9537d2e3bec687dffc9f41 Mon Sep 17 00:00:00 2001 From: can Date: Tue, 30 Apr 2019 11:20:41 +0300 Subject: [PATCH] Fixed #30412 -- Fixed crash when adding check constraints with OR'ed condition on Oracle and SQLite. --- django/db/models/sql/query.py | 2 +- docs/releases/2.2.1.txt | 3 +++ tests/migrations/test_operations.py | 23 +++++++++++++++++++++++ tests/queries/test_query.py | 15 +++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index b99f03cd88..d69c24419b 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1338,7 +1338,7 @@ class Query(BaseExpression): if isinstance(child, Node): child_clause, needed_inner = self._add_q( child, used_aliases, branch_negated, - current_negated, allow_joins, split_subq) + current_negated, allow_joins, split_subq, simple_col) joinpromoter.add_votes(needed_inner) else: child_clause, needed_inner = self.build_filter( diff --git a/docs/releases/2.2.1.txt b/docs/releases/2.2.1.txt index 90c9a06e79..38e8052b7d 100644 --- a/docs/releases/2.2.1.txt +++ b/docs/releases/2.2.1.txt @@ -74,3 +74,6 @@ Bugfixes * Fixed a migration crash on Oracle and PostgreSQL when adding a check constraint with a ``contains``, ``startswith``, or ``endswith`` lookup (or their case-insensitive variant) (:ticket:`30408`). + +* Fixed a migration crash on Oracle and SQLite when adding a check constraint + with ``condition`` contains ``|`` (``OR``) operator (:ticket:`30412`). diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index 520a2b2204..3b2129a933 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -1898,6 +1898,29 @@ class OperationTests(OperationTestBase): author = Author.objects.create(name='Albert', rebate='10%') self.assertEqual(Author.objects.get(), author) + @skipUnlessDBFeature('supports_table_check_constraints') + def test_add_or_constraint(self): + app_label = 'test_addorconstraint' + constraint_name = 'add_constraint_or' + from_state = self.set_up_test_model(app_label) + check = models.Q(pink__gt=2, weight__gt=2) | models.Q(weight__lt=0) + constraint = models.CheckConstraint(check=check, name=constraint_name) + operation = migrations.AddConstraint('Pony', constraint) + to_state = from_state.clone() + operation.state_forwards(app_label, to_state) + with connection.schema_editor() as editor: + operation.database_forwards(app_label, editor, from_state, to_state) + Pony = to_state.apps.get_model(app_label, 'Pony') + with self.assertRaises(IntegrityError), transaction.atomic(): + Pony.objects.create(pink=2, weight=3.0) + with self.assertRaises(IntegrityError), transaction.atomic(): + Pony.objects.create(pink=3, weight=1.0) + Pony.objects.bulk_create([ + Pony(pink=3, weight=-1.0), + Pony(pink=1, weight=-1.0), + Pony(pink=3, weight=3.0), + ]) + @skipUnlessDBFeature('supports_table_check_constraints') def test_remove_constraint(self): project_state = self.set_up_test_model("test_removeconstraint", constraints=[ diff --git a/tests/queries/test_query.py b/tests/queries/test_query.py index bef79c992e..c6a659fe97 100644 --- a/tests/queries/test_query.py +++ b/tests/queries/test_query.py @@ -23,6 +23,21 @@ class TestQuery(SimpleTestCase): self.assertEqual(lookup.rhs, 2) self.assertEqual(lookup.lhs.target, Author._meta.get_field('num')) + def test_simplecol_query(self): + query = Query(Author) + where = query.build_where(Q(num__gt=2, name__isnull=False) | Q(num__lt=F('id'))) + + name_isnull_lookup, num_gt_lookup = where.children[0].children + self.assertIsInstance(num_gt_lookup, GreaterThan) + self.assertIsInstance(num_gt_lookup.lhs, SimpleCol) + self.assertIsInstance(name_isnull_lookup, IsNull) + self.assertIsInstance(name_isnull_lookup.lhs, SimpleCol) + + num_lt_lookup = where.children[1] + self.assertIsInstance(num_lt_lookup, LessThan) + self.assertIsInstance(num_lt_lookup.rhs, SimpleCol) + self.assertIsInstance(num_lt_lookup.lhs, SimpleCol) + def test_complex_query(self): query = Query(Author) where = query.build_where(Q(num__gt=2) | Q(num__lt=0))