From 331a460f8f2e4f447b68fba491464b68c9b21fd1 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 14 Apr 2022 09:53:31 +0200 Subject: [PATCH] Fixed DatabaseFeatures.uses_savepoints/can_release_savepoints and related tests with MyISAM storage engine. --- django/db/backends/mysql/features.py | 3 +++ tests/admin_views/tests.py | 7 +++++-- tests/backends/mysql/test_features.py | 1 + tests/select_for_update/tests.py | 6 +++--- tests/transaction_hooks/tests.py | 1 + tests/transactions/tests.py | 1 + 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index a34741267ae..7b9a90ab065 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -305,6 +305,9 @@ class DatabaseFeatures(BaseDatabaseFeatures): """ return self._mysql_storage_engine != "MyISAM" + uses_savepoints = property(operator.attrgetter("supports_transactions")) + can_release_savepoints = property(operator.attrgetter("supports_transactions")) + @cached_property def ignores_table_name_case(self): return self.connection.mysql_server_data["lower_case_table_names"] diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 72fac695dd0..145add114cf 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -30,6 +30,7 @@ from django.contrib.contenttypes.models import ContentType from django.core import mail from django.core.checks import Error from django.core.files import temp as tempfile +from django.db import connection from django.forms.utils import ErrorList from django.template.response import TemplateResponse from django.test import ( @@ -7022,7 +7023,8 @@ class UserAdminTest(TestCase): # Don't depend on a warm cache, see #17377. ContentType.objects.clear_cache() - with self.assertNumQueries(10): + expected_num_queries = 10 if connection.features.uses_savepoints else 8 + with self.assertNumQueries(expected_num_queries): response = self.client.get(reverse("admin:auth_user_change", args=(u.pk,))) self.assertEqual(response.status_code, 200) @@ -7069,7 +7071,8 @@ class GroupAdminTest(TestCase): # Ensure no queries are skipped due to cached content type for Group. ContentType.objects.clear_cache() - with self.assertNumQueries(8): + expected_num_queries = 8 if connection.features.uses_savepoints else 6 + with self.assertNumQueries(expected_num_queries): response = self.client.get(reverse("admin:auth_group_change", args=(g.pk,))) self.assertEqual(response.status_code, 200) diff --git a/tests/backends/mysql/test_features.py b/tests/backends/mysql/test_features.py index ec5bd442fb5..88e267f0484 100644 --- a/tests/backends/mysql/test_features.py +++ b/tests/backends/mysql/test_features.py @@ -11,6 +11,7 @@ class TestFeatures(TestCase): """ All storage engines except MyISAM support transactions. """ + del connection.features.supports_transactions with mock.patch( "django.db.connection.features._mysql_storage_engine", "InnoDB" ): diff --git a/tests/select_for_update/tests.py b/tests/select_for_update/tests.py index 02510145415..5f5ada8939d 100644 --- a/tests/select_for_update/tests.py +++ b/tests/select_for_update/tests.py @@ -291,7 +291,7 @@ class SelectForUpdateTests(TransactionTestCase): qs = Person.objects.select_for_update(of=("self", "born")) self.assertIs(qs.exists(), True) - @skipUnlessDBFeature("has_select_for_update_nowait") + @skipUnlessDBFeature("has_select_for_update_nowait", "supports_transactions") def test_nowait_raises_error_on_block(self): """ If nowait is specified, we expect an error to be raised rather @@ -312,7 +312,7 @@ class SelectForUpdateTests(TransactionTestCase): self.end_blocking_transaction() self.assertIsInstance(status[-1], DatabaseError) - @skipUnlessDBFeature("has_select_for_update_skip_locked") + @skipUnlessDBFeature("has_select_for_update_skip_locked", "supports_transactions") def test_skip_locked_skips_locked_rows(self): """ If skip_locked is specified, the locked row is skipped resulting in @@ -599,7 +599,7 @@ class SelectForUpdateTests(TransactionTestCase): p = Person.objects.get(pk=self.person.pk) self.assertEqual("Fred", p.name) - @skipUnlessDBFeature("has_select_for_update") + @skipUnlessDBFeature("has_select_for_update", "supports_transactions") def test_raw_lock_not_available(self): """ Running a raw query which can't obtain a FOR UPDATE lock raises diff --git a/tests/transaction_hooks/tests.py b/tests/transaction_hooks/tests.py index 4a563088abe..75cac5a3e97 100644 --- a/tests/transaction_hooks/tests.py +++ b/tests/transaction_hooks/tests.py @@ -8,6 +8,7 @@ class ForcedError(Exception): pass +@skipUnlessDBFeature("supports_transactions") class TestConnectionOnCommit(TransactionTestCase): """ Tests for transaction.on_commit(). diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py index 342434666e7..a528ab22e5e 100644 --- a/tests/transactions/tests.py +++ b/tests/transactions/tests.py @@ -370,6 +370,7 @@ class AtomicErrorsTests(TransactionTestCase): self.assertEqual(Reporter.objects.count(), 0) +@skipUnlessDBFeature("uses_savepoints") @skipUnless(connection.vendor == "mysql", "MySQL-specific behaviors") class AtomicMySQLTests(TransactionTestCase):