java-topology/defects/django/patch/django-0006-remove-from-added-set.md

61 lines
2.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000379
# django-0006: remove_from_added / remove_from_removed lists → sets in create_altered_indexes()
## Severity
MEDIUM
## Location
`django/db/migrations/autodetector.py``create_altered_indexes()`
## Description
`remove_from_added` and `remove_from_removed` are built as plain `list`s and
appended to inside a double `for new_index / for old_index` loop. At the end,
two list-comprehension filters test `idx not in remove_from_added` and
`idx not in remove_from_removed` by scanning those lists linearly.
Worst-case per model: I added indexes × I removed indexes inner loop
produces up to I entries in each removal list; the two final comprehensions
then scan them O(I) per candidate → O(I²) total per model, O(N × I²) overall.
In a large monorepo with many indexes per model the autodetector fires on
every `makemigrations` invocation.
## Defective code (lines 13941456)
```python
remove_from_added = [] # ← plain list
remove_from_removed = [] # ← plain list
for new_index in added_indexes:
...
for old_index in removed_indexes:
...
if ...:
remove_from_added.append(new_index) # ← appended
remove_from_removed.append(old_index) # ← appended
added_indexes = [
idx for idx in added_indexes if idx not in remove_from_added # ← O(R) scan
]
removed_indexes = [
idx for idx in removed_indexes if idx not in remove_from_removed # ← O(R) scan
]
```
## Fix
```diff
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -1394,8 +1394,8 @@ class MigrationAutodetector:
- remove_from_added = []
- remove_from_removed = []
+ remove_from_added = set()
+ remove_from_removed = set()
...
- remove_from_added.append(new_index)
- remove_from_removed.append(old_index)
+ remove_from_added.add(new_index)
+ remove_from_removed.add(old_index)
```
## Complexity
- Before: O(N ×× I) = O(N × I³) (index objects must be hashable — they implement __hash__ via Index.name)
- After: O(N × I²) — membership tests drop to O(1)