diff options
| author | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-04-27 09:53:27 +0200 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-04-27 10:34:47 +0200 |
| commit | 4074f38e1dcc93b859bbbfd6abd8441c3bca36b3 (patch) | |
| tree | 29e45209e4d21b1e641b201ad953411449a1fb9b /django/db/models/sql | |
| parent | 158eca4f93e492a6cc6f0240a0b6da3b7aa98ee6 (diff) | |
Refs #32682 -- Fixed QuerySet.delete() crash on querysets with self-referential subqueries on MySQL.
Diffstat (limited to 'django/db/models/sql')
| -rw-r--r-- | django/db/models/sql/compiler.py | 20 |
1 files changed, 19 insertions, 1 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 3b66426f21..5fd37423de 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -1452,6 +1452,24 @@ class SQLDeleteCompiler(SQLCompiler): self.query.get_initial_alias() return sum(self.query.alias_refcount[t] > 0 for t in self.query.alias_map) == 1 + @classmethod + def _expr_refs_base_model(cls, expr, base_model): + if isinstance(expr, Query): + return expr.model == base_model + if not hasattr(expr, 'get_source_expressions'): + return False + return any( + cls._expr_refs_base_model(source_expr, base_model) + for source_expr in expr.get_source_expressions() + ) + + @cached_property + def contains_self_reference_subquery(self): + return any( + self._expr_refs_base_model(expr, self.query.model) + for expr in chain(self.query.annotations.values(), self.query.where.children) + ) + def _as_sql(self, query): result = [ 'DELETE FROM %s' % self.quote_name_unless_alias(query.base_table) @@ -1466,7 +1484,7 @@ class SQLDeleteCompiler(SQLCompiler): Create the SQL for this query. Return the SQL string and list of parameters. """ - if self.single_alias: + if self.single_alias and not self.contains_self_reference_subquery: return self._as_sql(self.query) innerq = self.query.clone() innerq.__class__ = Query |
