mirror of https://github.com/django/django.git
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.
This commit is contained in:
parent
97c05a64ca
commit
53ea4cce2f
|
@ -1021,11 +1021,21 @@ class Query(BaseExpression):
|
||||||
if alias == old_alias:
|
if alias == old_alias:
|
||||||
table_aliases[pos] = new_alias
|
table_aliases[pos] = new_alias
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# 3. Rename the direct external aliases and the ones of combined
|
||||||
|
# queries (union, intersection, difference).
|
||||||
self.external_aliases = {
|
self.external_aliases = {
|
||||||
# Table is aliased or it's being changed and thus is aliased.
|
# Table is aliased or it's being changed and thus is aliased.
|
||||||
change_map.get(alias, alias): (aliased or alias in change_map)
|
change_map.get(alias, alias): (aliased or alias in change_map)
|
||||||
for alias, aliased in self.external_aliases.items()
|
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):
|
def bump_prefix(self, other_query, exclude=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -14,7 +14,16 @@ from django.db.models.functions import Mod
|
||||||
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||||
from django.test.utils import CaptureQueriesContext
|
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")
|
@skipUnlessDBFeature("supports_select_union")
|
||||||
|
@ -450,6 +459,27 @@ class QuerySetSetOperationTests(TestCase):
|
||||||
[8, 1],
|
[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):
|
def test_union_in_subquery_related_outerref(self):
|
||||||
e1 = ExtraInfo.objects.create(value=7, info="e3")
|
e1 = ExtraInfo.objects.create(value=7, info="e3")
|
||||||
e2 = ExtraInfo.objects.create(value=5, info="e2")
|
e2 = ExtraInfo.objects.create(value=5, info="e2")
|
||||||
|
|
Loading…
Reference in New Issue