summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author4the4ryushin <aman2001mi@gmail.com>2023-04-28 02:33:25 +0530
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-05-01 10:20:20 +0200
commit0b0998dc151feb77068e2387c34cc50ef6b356ae (patch)
tree89076756b666cc024ec22d81f299400cdf52aaad
parent5a6d4d3bfde07daab9777545694beb014c832264 (diff)
downloaddjango-0b0998dc151feb77068e2387c34cc50ef6b356ae.tar.gz
Fixed #33759 -- Avoided unnecessary subquery in QuerySet.delete() with self-referential subqueries if supported.
-rw-r--r--django/db/backends/base/features.py3
-rw-r--r--django/db/backends/mysql/features.py1
-rw-r--r--django/db/models/sql/compiler.py5
-rw-r--r--tests/delete_regress/tests.py21
4 files changed, 21 insertions, 9 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py
index 63b3ab7710..11fa807c1b 100644
--- a/django/db/backends/base/features.py
+++ b/django/db/backends/base/features.py
@@ -12,6 +12,9 @@ class BaseDatabaseFeatures:
allows_group_by_select_index = True
empty_fetchmany_value = []
update_can_self_select = True
+ # Does the backend support self-reference subqueries in the DELETE
+ # statement?
+ delete_can_self_reference_subquery = True
# Does the backend distinguish between '' and None?
interprets_empty_strings_as_nulls = False
diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py
index 0b8d12d2fe..739ab380e0 100644
--- a/django/db/backends/mysql/features.py
+++ b/django/db/backends/mysql/features.py
@@ -24,6 +24,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_slicing_ordering_in_compound = True
supports_index_on_text_field = False
supports_update_conflicts = True
+ delete_can_self_reference_subquery = False
create_test_procedure_without_params_sql = """
CREATE PROCEDURE test_procedure ()
BEGIN
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 07393e7605..a7a8c98b99 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -1890,7 +1890,10 @@ class SQLDeleteCompiler(SQLCompiler):
Create the SQL for this query. Return the SQL string and list of
parameters.
"""
- if self.single_alias and not self.contains_self_reference_subquery:
+ if self.single_alias and (
+ self.connection.features.delete_can_self_reference_subquery
+ or not self.contains_self_reference_subquery
+ ):
return self._as_sql(self.query)
innerq = self.query.clone()
innerq.__class__ = Query
diff --git a/tests/delete_regress/tests.py b/tests/delete_regress/tests.py
index 173f767b28..2540fd2802 100644
--- a/tests/delete_regress/tests.py
+++ b/tests/delete_regress/tests.py
@@ -379,15 +379,20 @@ class DeleteTests(TestCase):
child = Child.objects.create(name="Juan")
Book.objects.create(pagecount=500, owner=child)
PlayedWith.objects.create(child=child, toy=toy, date=datetime.date.today())
- Book.objects.filter(
- Exists(
- Book.objects.filter(
- pk=OuterRef("pk"),
- owner__toys=toy.pk,
- ),
- )
- ).delete()
+ with self.assertNumQueries(1) as ctx:
+ Book.objects.filter(
+ Exists(
+ Book.objects.filter(
+ pk=OuterRef("pk"),
+ owner__toys=toy.pk,
+ ),
+ )
+ ).delete()
+
self.assertIs(Book.objects.exists(), False)
+ sql = ctx.captured_queries[0]["sql"].lower()
+ if connection.features.delete_can_self_reference_subquery:
+ self.assertEqual(sql.count("select"), 1)
class DeleteDistinct(SimpleTestCase):