summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2024-10-14 19:21:48 -0400
committerGitHub <noreply@github.com>2024-10-14 20:21:48 -0300
commit53ea4cce2fd08e84b9cdb6363267ccb9525f7060 (patch)
tree888b326677c393fcdc6a65bcae96857f484cf7b1
parent97c05a64ca87253e9789ebaab4b6d20a1b2370cf (diff)
Fixed #35744 -- Relabelled external aliases of combined queries.
Just like normal queries, combined queries' outer references might fully resolve before their reference is assigned its final alias. Refs #29338. Thanks Antony_K for the report and example, and thanks Mariusz Felisiak for the review.
-rw-r--r--django/db/models/sql/query.py10
-rw-r--r--tests/queries/test_qs_combinators.py32
2 files changed, 41 insertions, 1 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index aef3f48f10..b7b93c235a 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1021,11 +1021,21 @@ class Query(BaseExpression):
if alias == old_alias:
table_aliases[pos] = new_alias
break
+
+ # 3. Rename the direct external aliases and the ones of combined
+ # queries (union, intersection, difference).
self.external_aliases = {
# Table is aliased or it's being changed and thus is aliased.
change_map.get(alias, alias): (aliased or alias in change_map)
for alias, aliased in self.external_aliases.items()
}
+ for combined_query in self.combined_queries:
+ external_change_map = {
+ alias: aliased
+ for alias, aliased in change_map.items()
+ if alias in combined_query.external_aliases
+ }
+ combined_query.change_aliases(external_change_map)
def bump_prefix(self, other_query, exclude=None):
"""
diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py
index ad1017c8af..2f6e93cde8 100644
--- a/tests/queries/test_qs_combinators.py
+++ b/tests/queries/test_qs_combinators.py
@@ -14,7 +14,16 @@ from django.db.models.functions import Mod
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.test.utils import CaptureQueriesContext
-from .models import Author, Celebrity, ExtraInfo, Number, ReservedName
+from .models import (
+ Annotation,
+ Author,
+ Celebrity,
+ ExtraInfo,
+ Note,
+ Number,
+ ReservedName,
+ Tag,
+)
@skipUnlessDBFeature("supports_select_union")
@@ -450,6 +459,27 @@ class QuerySetSetOperationTests(TestCase):
[8, 1],
)
+ @skipUnlessDBFeature("supports_select_intersection")
+ def test_intersection_in_nested_subquery(self):
+ tag = Tag.objects.create(name="tag")
+ note = Note.objects.create(tag=tag)
+ annotation = Annotation.objects.create(tag=tag)
+ tags = Tag.objects.order_by()
+ tags = tags.filter(id=OuterRef("tag_id")).intersection(
+ tags.filter(id=OuterRef(OuterRef("tag_id")))
+ )
+ qs = Note.objects.filter(
+ Exists(
+ Annotation.objects.filter(
+ Exists(tags),
+ notes__in=OuterRef("pk"),
+ )
+ )
+ )
+ self.assertIsNone(qs.first())
+ annotation.notes.add(note)
+ self.assertEqual(qs.first(), note)
+
def test_union_in_subquery_related_outerref(self):
e1 = ExtraInfo.objects.create(value=7, info="e3")
e2 = ExtraInfo.objects.create(value=5, info="e2")