1"""
2Create SQL statements for QuerySets.
3
4The code in here encapsulates all of the SQL construction so that QuerySets
5themselves do not have to (and could be backed by things other than SQL
6databases). The abstraction barrier only works one way: this module has to know
7all about the internals of models in order to get the information it needs.
8"""
9
10from __future__ import annotations
11
12import copy
13import difflib
14import functools
15import sys
16from collections import Counter, namedtuple
17from collections.abc import Iterable, Iterator, Mapping
18from collections.abc import Iterator as TypingIterator
19from functools import cached_property
20from itertools import chain, count, product
21from string import ascii_uppercase
22from typing import TYPE_CHECKING, Any, Literal, TypeVar, overload
23
24if TYPE_CHECKING:
25 from typing import Self
26
27from plain.models.aggregates import Count
28from plain.models.constants import LOOKUP_SEP
29from plain.models.db import NotSupportedError, db_connection
30from plain.models.exceptions import FieldDoesNotExist, FieldError
31from plain.models.expressions import (
32 BaseExpression,
33 Col,
34 Exists,
35 F,
36 OuterRef,
37 Ref,
38 ResolvedOuterRef,
39 Value,
40)
41from plain.models.fields import Field
42from plain.models.fields.related_lookups import MultiColSource
43from plain.models.lookups import Lookup
44from plain.models.query_utils import (
45 PathInfo,
46 Q,
47 check_rel_lookup_compatibility,
48 refs_expression,
49)
50from plain.models.sql.constants import INNER, LOUTER, ORDER_DIR, SINGLE
51from plain.models.sql.datastructures import BaseTable, Empty, Join, MultiJoin
52from plain.models.sql.where import AND, OR, ExtraWhere, NothingNode, WhereNode
53from plain.utils.regex_helper import _lazy_re_compile
54from plain.utils.tree import Node
55
56if TYPE_CHECKING:
57 from plain.models import Model
58 from plain.models.backends.base.base import BaseDatabaseWrapper
59 from plain.models.meta import Meta
60 from plain.models.sql.compiler import SQLCompiler
61
62
63__all__ = ["Query", "RawQuery"]
64
65# Quotation marks ('"`[]), whitespace characters, semicolons, or inline
66# SQL comments are forbidden in column aliases.
67FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|--|/\*|\*/")
68
69# Inspired from
70# https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
71EXPLAIN_OPTIONS_PATTERN = _lazy_re_compile(r"[\w\-]+")
72
73
74def get_field_names_from_opts(meta: Meta | None) -> set[str]:
75 if meta is None:
76 return set()
77 return set(
78 chain.from_iterable(
79 (f.name, f.attname) if f.concrete else (f.name,) for f in meta.get_fields()
80 )
81 )
82
83
84def get_children_from_q(q: Q) -> TypingIterator[tuple[str, Any]]:
85 for child in q.children:
86 if isinstance(child, Node):
87 yield from get_children_from_q(child)
88 else:
89 yield child
90
91
92JoinInfo = namedtuple(
93 "JoinInfo",
94 ("final_field", "targets", "meta", "joins", "path", "transform_function"),
95)
96
97
98class RawQuery:
99 """A single raw SQL query."""
100
101 def __init__(self, sql: str, params: tuple[Any, ...] | dict[str, Any] = ()):
102 self.params = params
103 self.sql = sql
104 self.cursor: Any = None
105
106 # Mirror some properties of a normal query so that
107 # the compiler can be used to process results.
108 self.low_mark, self.high_mark = 0, None # Used for offset/limit
109 self.extra_select = {}
110 self.annotation_select = {}
111
112 def chain(self) -> RawQuery:
113 return self.clone()
114
115 def clone(self) -> RawQuery:
116 return RawQuery(self.sql, params=self.params)
117
118 def get_columns(self) -> list[str]:
119 if self.cursor is None:
120 self._execute_query()
121 converter = db_connection.introspection.identifier_converter
122 return [
123 converter(column_model_meta[0])
124 for column_model_meta in self.cursor.description
125 ]
126
127 def __iter__(self) -> TypingIterator[Any]:
128 # Always execute a new query for a new iterator.
129 # This could be optimized with a cache at the expense of RAM.
130 self._execute_query()
131 if not db_connection.features.can_use_chunked_reads:
132 # If the database can't use chunked reads we need to make sure we
133 # evaluate the entire query up front.
134 result = list(self.cursor)
135 else:
136 result = self.cursor
137 return iter(result)
138
139 def __repr__(self) -> str:
140 return f"<{self.__class__.__name__}: {self}>"
141
142 @property
143 def params_type(self) -> type[dict] | type[tuple] | None:
144 if self.params is None:
145 return None
146 return dict if isinstance(self.params, Mapping) else tuple
147
148 def __str__(self) -> str:
149 if self.params_type is None:
150 return self.sql
151 return self.sql % self.params_type(self.params)
152
153 def _execute_query(self) -> None:
154 # Adapt parameters to the database, as much as possible considering
155 # that the target type isn't known. See #17755.
156 params_type = self.params_type
157 adapter = db_connection.ops.adapt_unknown_value
158 if params_type is tuple:
159 params = tuple(adapter(val) for val in self.params)
160 elif params_type is dict:
161 params = {key: adapter(val) for key, val in self.params.items()} # type: ignore[union-attr]
162 elif params_type is None:
163 params = None
164 else:
165 raise RuntimeError(f"Unexpected params type: {params_type}")
166
167 self.cursor = db_connection.cursor()
168 self.cursor.execute(self.sql, params)
169
170
171ExplainInfo = namedtuple("ExplainInfo", ("format", "options"))
172
173QueryType = TypeVar("QueryType", bound="Query")
174
175
176class Query(BaseExpression):
177 """A single SQL query."""
178
179 alias_prefix = "T"
180 empty_result_set_value = None
181 subq_aliases = frozenset([alias_prefix])
182
183 compiler = "SQLCompiler"
184
185 base_table_class = BaseTable
186 join_class = Join
187
188 default_cols = True
189 default_ordering = True
190 standard_ordering = True
191
192 filter_is_sticky = False
193 subquery = False
194
195 # SQL-related attributes.
196 # Select and related select clauses are expressions to use in the SELECT
197 # clause of the query. The select is used for cases where we want to set up
198 # the select clause to contain other than default fields (values(),
199 # subqueries...). Note that annotations go to annotations dictionary.
200 select = ()
201 # The group_by attribute can have one of the following forms:
202 # - None: no group by at all in the query
203 # - A tuple of expressions: group by (at least) those expressions.
204 # String refs are also allowed for now.
205 # - True: group by all select fields of the model
206 # See compiler.get_group_by() for details.
207 group_by = None
208 order_by = ()
209 low_mark = 0 # Used for offset/limit.
210 high_mark = None # Used for offset/limit.
211 distinct = False
212 distinct_fields = ()
213 select_for_update = False
214 select_for_update_nowait = False
215 select_for_update_skip_locked = False
216 select_for_update_of = ()
217 select_for_no_key_update = False
218 select_related: bool | dict[str, Any] = False
219 has_select_fields = False
220 # Arbitrary limit for select_related to prevents infinite recursion.
221 max_depth = 5
222 # Holds the selects defined by a call to values() or values_list()
223 # excluding annotation_select and extra_select.
224 values_select = ()
225
226 # SQL annotation-related attributes.
227 annotation_select_mask = None
228 _annotation_select_cache = None
229
230 # Set combination attributes.
231 combinator = None
232 combinator_all = False
233 combined_queries = ()
234
235 # These are for extensions. The contents are more or less appended verbatim
236 # to the appropriate clause.
237 extra_select_mask = None
238 _extra_select_cache = None
239
240 extra_tables = ()
241 extra_order_by = ()
242
243 # A tuple that is a set of model field names and either True, if these are
244 # the fields to defer, or False if these are the only fields to load.
245 deferred_loading = (frozenset(), True)
246
247 explain_info = None
248
249 def __init__(self, model: type[Model], alias_cols: bool = True):
250 self.model = model
251 self.alias_refcount = {}
252 # alias_map is the most important data structure regarding joins.
253 # It's used for recording which joins exist in the query and what
254 # types they are. The key is the alias of the joined table (possibly
255 # the table name) and the value is a Join-like object (see
256 # sql.datastructures.Join for more information).
257 self.alias_map = {}
258 # Whether to provide alias to columns during reference resolving.
259 self.alias_cols = alias_cols
260 # Sometimes the query contains references to aliases in outer queries (as
261 # a result of split_exclude). Correct alias quoting needs to know these
262 # aliases too.
263 # Map external tables to whether they are aliased.
264 self.external_aliases = {}
265 self.table_map = {} # Maps table names to list of aliases.
266 self.used_aliases = set()
267
268 self.where = WhereNode()
269 # Maps alias -> Annotation Expression.
270 self.annotations = {}
271 # These are for extensions. The contents are more or less appended
272 # verbatim to the appropriate clause.
273 self.extra = {} # Maps col_alias -> (col_sql, params).
274
275 self._filtered_relations = {}
276
277 @property
278 def output_field(self) -> Field | None: # type: ignore[return]
279 if len(self.select) == 1:
280 select = self.select[0]
281 return getattr(select, "target", None) or select.field
282 elif len(self.annotation_select) == 1:
283 return next(iter(self.annotation_select.values())).output_field
284
285 @cached_property
286 def base_table(self) -> str | None:
287 for alias in self.alias_map:
288 return alias
289
290 def __str__(self) -> str:
291 """
292 Return the query as a string of SQL with the parameter values
293 substituted in (use sql_with_params() to see the unsubstituted string).
294
295 Parameter values won't necessarily be quoted correctly, since that is
296 done by the database interface at execution time.
297 """
298 sql, params = self.sql_with_params()
299 return sql % params
300
301 def sql_with_params(self) -> tuple[str, tuple[Any, ...]]:
302 """
303 Return the query as an SQL string and the parameters that will be
304 substituted into the query.
305 """
306 return self.get_compiler().as_sql()
307
308 def __deepcopy__(self, memo: dict[int, Any]) -> Self:
309 """Limit the amount of work when a Query is deepcopied."""
310 result = self.clone()
311 memo[id(self)] = result
312 return result
313
314 def get_compiler(self, *, elide_empty: bool = True) -> Any:
315 return db_connection.ops.compiler(self.compiler)(
316 self, db_connection, elide_empty
317 )
318
319 def get_model_meta(self) -> Meta:
320 """
321 Return the Meta instance (the model._model_meta) from which to start
322 processing. Normally, this is self.model._model_meta, but it can be changed
323 by subclasses.
324 """
325 return self.model._model_meta
326
327 def clone(self) -> Self:
328 """
329 Return a copy of the current Query. A lightweight alternative to
330 deepcopy().
331 """
332 obj = Empty() # type: ignore[misc]
333 obj.__class__ = self.__class__ # type: ignore[misc]
334 # Copy references to everything.
335 obj.__dict__ = self.__dict__.copy() # type: ignore[attr-defined]
336 # Clone attributes that can't use shallow copy.
337 obj.alias_refcount = self.alias_refcount.copy() # type: ignore[attr-defined]
338 obj.alias_map = self.alias_map.copy() # type: ignore[attr-defined]
339 obj.external_aliases = self.external_aliases.copy() # type: ignore[attr-defined]
340 obj.table_map = self.table_map.copy() # type: ignore[attr-defined]
341 obj.where = self.where.clone() # type: ignore[attr-defined]
342 obj.annotations = self.annotations.copy() # type: ignore[attr-defined]
343 if self.annotation_select_mask is not None:
344 obj.annotation_select_mask = self.annotation_select_mask.copy() # type: ignore[attr-defined]
345 if self.combined_queries:
346 obj.combined_queries = tuple( # type: ignore[attr-defined]
347 [query.clone() for query in self.combined_queries]
348 )
349 # _annotation_select_cache cannot be copied, as doing so breaks the
350 # (necessary) state in which both annotations and
351 # _annotation_select_cache point to the same underlying objects.
352 # It will get re-populated in the cloned queryset the next time it's
353 # used.
354 obj._annotation_select_cache = None # type: ignore[attr-defined]
355 obj.extra = self.extra.copy() # type: ignore[attr-defined]
356 if self.extra_select_mask is not None:
357 obj.extra_select_mask = self.extra_select_mask.copy() # type: ignore[attr-defined]
358 if self._extra_select_cache is not None:
359 obj._extra_select_cache = self._extra_select_cache.copy() # type: ignore[attr-defined]
360 if self.select_related is not False:
361 # Use deepcopy because select_related stores fields in nested
362 # dicts.
363 obj.select_related = copy.deepcopy(obj.select_related) # type: ignore[attr-defined]
364 if "subq_aliases" in self.__dict__:
365 obj.subq_aliases = self.subq_aliases.copy() # type: ignore[attr-defined]
366 obj.used_aliases = self.used_aliases.copy() # type: ignore[attr-defined]
367 obj._filtered_relations = self._filtered_relations.copy() # type: ignore[attr-defined]
368 # Clear the cached_property, if it exists.
369 obj.__dict__.pop("base_table", None)
370 return obj # type: ignore[return-value]
371
372 @overload
373 def chain(self, klass: None = None) -> Self: ...
374
375 @overload
376 def chain(self, klass: type[QueryType]) -> QueryType: ...
377
378 def chain(self, klass: type[Query] | None = None) -> Query:
379 """
380 Return a copy of the current Query that's ready for another operation.
381 The klass argument changes the type of the Query, e.g. UpdateQuery.
382 """
383 obj = self.clone()
384 if klass and obj.__class__ != klass:
385 obj.__class__ = klass # type: ignore[misc]
386 if not obj.filter_is_sticky:
387 obj.used_aliases = set() # type: ignore[attr-defined]
388 obj.filter_is_sticky = False
389 if hasattr(obj, "_setup_query"):
390 obj._setup_query() # type: ignore[attr-defined]
391 return obj # type: ignore[return-value]
392
393 def relabeled_clone(self, change_map: dict[str, str]) -> Self:
394 clone = self.clone()
395 clone.change_aliases(change_map)
396 return clone
397
398 def _get_col(self, target: Any, field: Field, alias: str | None) -> Col:
399 if not self.alias_cols:
400 alias = None
401 return target.get_col(alias, field)
402
403 def get_aggregation(self, aggregate_exprs: dict[str, Any]) -> dict[str, Any]:
404 """
405 Return the dictionary with the values of the existing aggregations.
406 """
407 if not aggregate_exprs:
408 return {}
409 aggregates = {}
410 for alias, aggregate_expr in aggregate_exprs.items():
411 self.check_alias(alias)
412 aggregate = aggregate_expr.resolve_expression(
413 self, allow_joins=True, reuse=None, summarize=True
414 )
415 if not aggregate.contains_aggregate:
416 raise TypeError(f"{alias} is not an aggregate expression")
417 aggregates[alias] = aggregate
418 # Existing usage of aggregation can be determined by the presence of
419 # selected aggregates but also by filters against aliased aggregates.
420 _, having, qualify = self.where.split_having_qualify()
421 has_existing_aggregation = (
422 any(
423 getattr(annotation, "contains_aggregate", True)
424 for annotation in self.annotations.values()
425 )
426 or having
427 )
428 # Decide if we need to use a subquery.
429 #
430 # Existing aggregations would cause incorrect results as
431 # get_aggregation() must produce just one result and thus must not use
432 # GROUP BY.
433 #
434 # If the query has limit or distinct, or uses set operations, then
435 # those operations must be done in a subquery so that the query
436 # aggregates on the limit and/or distinct results instead of applying
437 # the distinct and limit after the aggregation.
438 if (
439 isinstance(self.group_by, tuple)
440 or self.is_sliced
441 or has_existing_aggregation
442 or qualify
443 or self.distinct
444 or self.combinator
445 ):
446 from plain.models.sql.subqueries import AggregateQuery
447
448 inner_query = self.clone()
449 inner_query.subquery = True
450 outer_query = AggregateQuery(self.model, inner_query)
451 inner_query.select_for_update = False
452 inner_query.select_related = False
453 inner_query.set_annotation_mask(self.annotation_select)
454 # Queries with distinct_fields need ordering and when a limit is
455 # applied we must take the slice from the ordered query. Otherwise
456 # no need for ordering.
457 inner_query.clear_ordering(force=False)
458 if not inner_query.distinct:
459 # If the inner query uses default select and it has some
460 # aggregate annotations, then we must make sure the inner
461 # query is grouped by the main model's primary key. However,
462 # clearing the select clause can alter results if distinct is
463 # used.
464 if inner_query.default_cols and has_existing_aggregation:
465 inner_query.group_by = (
466 self.model._model_meta.get_field("id").get_col(
467 inner_query.get_initial_alias()
468 ),
469 )
470 inner_query.default_cols = False
471 if not qualify:
472 # Mask existing annotations that are not referenced by
473 # aggregates to be pushed to the outer query unless
474 # filtering against window functions is involved as it
475 # requires complex realising.
476 annotation_mask = set()
477 for aggregate in aggregates.values():
478 annotation_mask |= aggregate.get_refs()
479 inner_query.set_annotation_mask(annotation_mask)
480
481 # Add aggregates to the outer AggregateQuery. This requires making
482 # sure all columns referenced by the aggregates are selected in the
483 # inner query. It is achieved by retrieving all column references
484 # by the aggregates, explicitly selecting them in the inner query,
485 # and making sure the aggregates are repointed to them.
486 col_refs = {}
487 for alias, aggregate in aggregates.items():
488 replacements = {}
489 for col in self._gen_cols([aggregate], resolve_refs=False):
490 if not (col_ref := col_refs.get(col)):
491 index = len(col_refs) + 1
492 col_alias = f"__col{index}"
493 col_ref = Ref(col_alias, col)
494 col_refs[col] = col_ref
495 inner_query.annotations[col_alias] = col
496 inner_query.append_annotation_mask([col_alias])
497 replacements[col] = col_ref
498 outer_query.annotations[alias] = aggregate.replace_expressions(
499 replacements
500 )
501 if (
502 inner_query.select == ()
503 and not inner_query.default_cols
504 and not inner_query.annotation_select_mask
505 ):
506 # In case of Model.objects[0:3].count(), there would be no
507 # field selected in the inner query, yet we must use a subquery.
508 # So, make sure at least one field is selected.
509 inner_query.select = (
510 self.model._model_meta.get_field("id").get_col(
511 inner_query.get_initial_alias()
512 ),
513 )
514 else:
515 outer_query = self
516 self.select = ()
517 self.default_cols = False
518 self.extra = {}
519 if self.annotations:
520 # Inline reference to existing annotations and mask them as
521 # they are unnecessary given only the summarized aggregations
522 # are requested.
523 replacements = {
524 Ref(alias, annotation): annotation
525 for alias, annotation in self.annotations.items()
526 }
527 self.annotations = {
528 alias: aggregate.replace_expressions(replacements)
529 for alias, aggregate in aggregates.items()
530 }
531 else:
532 self.annotations = aggregates
533 self.set_annotation_mask(aggregates)
534
535 empty_set_result = [
536 expression.empty_result_set_value
537 for expression in outer_query.annotation_select.values()
538 ]
539 elide_empty = not any(result is NotImplemented for result in empty_set_result)
540 outer_query.clear_ordering(force=True)
541 outer_query.clear_limits()
542 outer_query.select_for_update = False
543 outer_query.select_related = False
544 compiler = outer_query.get_compiler(elide_empty=elide_empty)
545 result = compiler.execute_sql(SINGLE)
546 if result is None:
547 result = empty_set_result
548 else:
549 converters = compiler.get_converters(outer_query.annotation_select.values())
550 result = next(compiler.apply_converters((result,), converters))
551
552 return dict(zip(outer_query.annotation_select, result))
553
554 def get_count(self) -> int:
555 """
556 Perform a COUNT() query using the current filter constraints.
557 """
558 obj = self.clone()
559 return obj.get_aggregation({"__count": Count("*")})["__count"]
560
561 def has_filters(self) -> bool:
562 return bool(self.where)
563
564 def exists(self, limit: bool = True) -> Self:
565 q = self.clone()
566 if not (q.distinct and q.is_sliced):
567 if q.group_by is True:
568 q.add_fields(
569 (f.attname for f in self.model._model_meta.concrete_fields), False
570 )
571 # Disable GROUP BY aliases to avoid orphaning references to the
572 # SELECT clause which is about to be cleared.
573 q.set_group_by(allow_aliases=False)
574 q.clear_select_clause()
575 if q.combined_queries and q.combinator == "union":
576 q.combined_queries = tuple(
577 combined_query.exists(limit=False)
578 for combined_query in q.combined_queries
579 )
580 q.clear_ordering(force=True)
581 if limit:
582 q.set_limits(high=1)
583 q.add_annotation(Value(1), "a")
584 return q
585
586 def has_results(self) -> bool:
587 q = self.exists()
588 compiler = q.get_compiler()
589 return compiler.has_results()
590
591 def explain(self, format: str | None = None, **options: Any) -> str:
592 q = self.clone()
593 for option_name in options:
594 if (
595 not EXPLAIN_OPTIONS_PATTERN.fullmatch(option_name)
596 or "--" in option_name
597 ):
598 raise ValueError(f"Invalid option name: {option_name!r}.")
599 q.explain_info = ExplainInfo(format, options)
600 compiler = q.get_compiler()
601 return "\n".join(compiler.explain_query())
602
603 def combine(self, rhs: Query, connector: str) -> None:
604 """
605 Merge the 'rhs' query into the current one (with any 'rhs' effects
606 being applied *after* (that is, "to the right of") anything in the
607 current query. 'rhs' is not modified during a call to this function.
608
609 The 'connector' parameter describes how to connect filters from the
610 'rhs' query.
611 """
612 if self.model != rhs.model:
613 raise TypeError("Cannot combine queries on two different base models.")
614 if self.is_sliced:
615 raise TypeError("Cannot combine queries once a slice has been taken.")
616 if self.distinct != rhs.distinct:
617 raise TypeError("Cannot combine a unique query with a non-unique query.")
618 if self.distinct_fields != rhs.distinct_fields:
619 raise TypeError("Cannot combine queries with different distinct fields.")
620
621 # If lhs and rhs shares the same alias prefix, it is possible to have
622 # conflicting alias changes like T4 -> T5, T5 -> T6, which might end up
623 # as T4 -> T6 while combining two querysets. To prevent this, change an
624 # alias prefix of the rhs and update current aliases accordingly,
625 # except if the alias is the base table since it must be present in the
626 # query on both sides.
627 initial_alias = self.get_initial_alias()
628 assert initial_alias is not None
629 rhs.bump_prefix(self, exclude={initial_alias})
630
631 # Work out how to relabel the rhs aliases, if necessary.
632 change_map = {}
633 conjunction = connector == AND
634
635 # Determine which existing joins can be reused. When combining the
636 # query with AND we must recreate all joins for m2m filters. When
637 # combining with OR we can reuse joins. The reason is that in AND
638 # case a single row can't fulfill a condition like:
639 # revrel__col=1 & revrel__col=2
640 # But, there might be two different related rows matching this
641 # condition. In OR case a single True is enough, so single row is
642 # enough, too.
643 #
644 # Note that we will be creating duplicate joins for non-m2m joins in
645 # the AND case. The results will be correct but this creates too many
646 # joins. This is something that could be fixed later on.
647 reuse = set() if conjunction else set(self.alias_map)
648 joinpromoter = JoinPromoter(connector, 2, False)
649 joinpromoter.add_votes(
650 j for j in self.alias_map if self.alias_map[j].join_type == INNER
651 )
652 rhs_votes = set()
653 # Now, add the joins from rhs query into the new query (skipping base
654 # table).
655 rhs_tables = list(rhs.alias_map)[1:]
656 for alias in rhs_tables:
657 join = rhs.alias_map[alias]
658 # If the left side of the join was already relabeled, use the
659 # updated alias.
660 join = join.relabeled_clone(change_map)
661 new_alias = self.join(join, reuse=reuse)
662 if join.join_type == INNER:
663 rhs_votes.add(new_alias)
664 # We can't reuse the same join again in the query. If we have two
665 # distinct joins for the same connection in rhs query, then the
666 # combined query must have two joins, too.
667 reuse.discard(new_alias)
668 if alias != new_alias:
669 change_map[alias] = new_alias
670 if not rhs.alias_refcount[alias]:
671 # The alias was unused in the rhs query. Unref it so that it
672 # will be unused in the new query, too. We have to add and
673 # unref the alias so that join promotion has information of
674 # the join type for the unused alias.
675 self.unref_alias(new_alias)
676 joinpromoter.add_votes(rhs_votes)
677 joinpromoter.update_join_types(self)
678
679 # Combine subqueries aliases to ensure aliases relabelling properly
680 # handle subqueries when combining where and select clauses.
681 self.subq_aliases |= rhs.subq_aliases
682
683 # Now relabel a copy of the rhs where-clause and add it to the current
684 # one.
685 w = rhs.where.clone()
686 w.relabel_aliases(change_map)
687 self.where.add(w, connector)
688
689 # Selection columns and extra extensions are those provided by 'rhs'.
690 if rhs.select:
691 self.set_select([col.relabeled_clone(change_map) for col in rhs.select])
692 else:
693 self.select = ()
694
695 if connector == OR:
696 # It would be nice to be able to handle this, but the queries don't
697 # really make sense (or return consistent value sets). Not worth
698 # the extra complexity when you can write a real query instead.
699 if self.extra and rhs.extra:
700 raise ValueError(
701 "When merging querysets using 'or', you cannot have "
702 "extra(select=...) on both sides."
703 )
704 self.extra.update(rhs.extra)
705 extra_select_mask = set()
706 if self.extra_select_mask is not None:
707 extra_select_mask.update(self.extra_select_mask)
708 if rhs.extra_select_mask is not None:
709 extra_select_mask.update(rhs.extra_select_mask)
710 if extra_select_mask:
711 self.set_extra_mask(extra_select_mask)
712 self.extra_tables += rhs.extra_tables
713
714 # Ordering uses the 'rhs' ordering, unless it has none, in which case
715 # the current ordering is used.
716 self.order_by = rhs.order_by or self.order_by
717 self.extra_order_by = rhs.extra_order_by or self.extra_order_by
718
719 def _get_defer_select_mask(
720 self,
721 meta: Meta,
722 mask: dict[str, Any],
723 select_mask: dict[Any, Any] | None = None,
724 ) -> dict[Any, Any]:
725 if select_mask is None:
726 select_mask = {}
727 select_mask[meta.get_field("id")] = {}
728 # All concrete fields that are not part of the defer mask must be
729 # loaded. If a relational field is encountered it gets added to the
730 # mask for it be considered if `select_related` and the cycle continues
731 # by recursively calling this function.
732 for field in meta.concrete_fields:
733 field_mask = mask.pop(field.name, None)
734 if field_mask is None:
735 select_mask.setdefault(field, {})
736 elif field_mask:
737 if not field.is_relation:
738 raise FieldError(next(iter(field_mask)))
739 field_select_mask = select_mask.setdefault(field, {})
740 related_model = field.remote_field.model
741 self._get_defer_select_mask(
742 related_model._model_meta, field_mask, field_select_mask
743 )
744 # Remaining defer entries must be references to reverse relationships.
745 # The following code is expected to raise FieldError if it encounters
746 # a malformed defer entry.
747 for field_name, field_mask in mask.items():
748 if filtered_relation := self._filtered_relations.get(field_name):
749 relation = meta.get_field(filtered_relation.relation_name)
750 field_select_mask = select_mask.setdefault((field_name, relation), {})
751 field = relation.field
752 else:
753 field = meta.get_field(field_name).field
754 field_select_mask = select_mask.setdefault(field, {})
755 related_model = field.model
756 self._get_defer_select_mask(
757 related_model._model_meta, field_mask, field_select_mask
758 )
759 return select_mask
760
761 def _get_only_select_mask(
762 self,
763 meta: Meta,
764 mask: dict[str, Any],
765 select_mask: dict[Any, Any] | None = None,
766 ) -> dict[Any, Any]:
767 if select_mask is None:
768 select_mask = {}
769 select_mask[meta.get_field("id")] = {}
770 # Only include fields mentioned in the mask.
771 for field_name, field_mask in mask.items():
772 field = meta.get_field(field_name)
773 field_select_mask = select_mask.setdefault(field, {})
774 if field_mask:
775 if not field.is_relation:
776 raise FieldError(next(iter(field_mask)))
777 related_model = field.remote_field.model
778 self._get_only_select_mask(
779 related_model._model_meta, field_mask, field_select_mask
780 )
781 return select_mask
782
783 def get_select_mask(self) -> dict[Any, Any]:
784 """
785 Convert the self.deferred_loading data structure to an alternate data
786 structure, describing the field that *will* be loaded. This is used to
787 compute the columns to select from the database and also by the
788 QuerySet class to work out which fields are being initialized on each
789 model. Models that have all their fields included aren't mentioned in
790 the result, only those that have field restrictions in place.
791 """
792 field_names, defer = self.deferred_loading
793 if not field_names:
794 return {}
795 mask = {}
796 for field_name in field_names:
797 part_mask = mask
798 for part in field_name.split(LOOKUP_SEP):
799 part_mask = part_mask.setdefault(part, {})
800 meta = self.get_model_meta()
801 if defer:
802 return self._get_defer_select_mask(meta, mask)
803 return self._get_only_select_mask(meta, mask)
804
805 def table_alias(
806 self, table_name: str, create: bool = False, filtered_relation: Any = None
807 ) -> tuple[str, bool]:
808 """
809 Return a table alias for the given table_name and whether this is a
810 new alias or not.
811
812 If 'create' is true, a new alias is always created. Otherwise, the
813 most recently created alias for the table (if one exists) is reused.
814 """
815 alias_list = self.table_map.get(table_name)
816 if not create and alias_list:
817 alias = alias_list[0]
818 self.alias_refcount[alias] += 1
819 return alias, False
820
821 # Create a new alias for this table.
822 if alias_list:
823 alias = "%s%d" % (self.alias_prefix, len(self.alias_map) + 1) # noqa: UP031
824 alias_list.append(alias)
825 else:
826 # The first occurrence of a table uses the table name directly.
827 alias = (
828 filtered_relation.alias if filtered_relation is not None else table_name
829 )
830 self.table_map[table_name] = [alias]
831 self.alias_refcount[alias] = 1
832 return alias, True
833
834 def ref_alias(self, alias: str) -> None:
835 """Increases the reference count for this alias."""
836 self.alias_refcount[alias] += 1
837
838 def unref_alias(self, alias: str, amount: int = 1) -> None:
839 """Decreases the reference count for this alias."""
840 self.alias_refcount[alias] -= amount
841
842 def promote_joins(self, aliases: set[str] | list[str]) -> None:
843 """
844 Promote recursively the join type of given aliases and its children to
845 an outer join. If 'unconditional' is False, only promote the join if
846 it is nullable or the parent join is an outer join.
847
848 The children promotion is done to avoid join chains that contain a LOUTER
849 b INNER c. So, if we have currently a INNER b INNER c and a->b is promoted,
850 then we must also promote b->c automatically, or otherwise the promotion
851 of a->b doesn't actually change anything in the query results.
852 """
853 aliases = list(aliases)
854 while aliases:
855 alias = aliases.pop(0)
856 if self.alias_map[alias].join_type is None:
857 # This is the base table (first FROM entry) - this table
858 # isn't really joined at all in the query, so we should not
859 # alter its join type.
860 continue
861 # Only the first alias (skipped above) should have None join_type
862 assert self.alias_map[alias].join_type is not None
863 parent_alias = self.alias_map[alias].parent_alias
864 parent_louter = (
865 parent_alias and self.alias_map[parent_alias].join_type == LOUTER
866 )
867 already_louter = self.alias_map[alias].join_type == LOUTER
868 if (self.alias_map[alias].nullable or parent_louter) and not already_louter:
869 self.alias_map[alias] = self.alias_map[alias].promote()
870 # Join type of 'alias' changed, so re-examine all aliases that
871 # refer to this one.
872 aliases.extend(
873 join
874 for join in self.alias_map
875 if self.alias_map[join].parent_alias == alias
876 and join not in aliases
877 )
878
879 def demote_joins(self, aliases: set[str] | list[str]) -> None:
880 """
881 Change join type from LOUTER to INNER for all joins in aliases.
882
883 Similarly to promote_joins(), this method must ensure no join chains
884 containing first an outer, then an inner join are generated. If we
885 are demoting b->c join in chain a LOUTER b LOUTER c then we must
886 demote a->b automatically, or otherwise the demotion of b->c doesn't
887 actually change anything in the query results. .
888 """
889 aliases = list(aliases)
890 while aliases:
891 alias = aliases.pop(0)
892 if self.alias_map[alias].join_type == LOUTER:
893 self.alias_map[alias] = self.alias_map[alias].demote()
894 parent_alias = self.alias_map[alias].parent_alias
895 if self.alias_map[parent_alias].join_type == INNER:
896 aliases.append(parent_alias)
897
898 def reset_refcounts(self, to_counts: dict[str, int]) -> None:
899 """
900 Reset reference counts for aliases so that they match the value passed
901 in `to_counts`.
902 """
903 for alias, cur_refcount in self.alias_refcount.copy().items():
904 unref_amount = cur_refcount - to_counts.get(alias, 0)
905 self.unref_alias(alias, unref_amount)
906
907 def change_aliases(self, change_map: dict[str, str]) -> None:
908 """
909 Change the aliases in change_map (which maps old-alias -> new-alias),
910 relabelling any references to them in select columns and the where
911 clause.
912 """
913 # If keys and values of change_map were to intersect, an alias might be
914 # updated twice (e.g. T4 -> T5, T5 -> T6, so also T4 -> T6) depending
915 # on their order in change_map.
916 assert set(change_map).isdisjoint(change_map.values())
917
918 # 1. Update references in "select" (normal columns plus aliases),
919 # "group by" and "where".
920 self.where.relabel_aliases(change_map)
921 if isinstance(self.group_by, tuple):
922 self.group_by = tuple(
923 [col.relabeled_clone(change_map) for col in self.group_by]
924 )
925 self.select = tuple([col.relabeled_clone(change_map) for col in self.select])
926 self.annotations = self.annotations and {
927 key: col.relabeled_clone(change_map)
928 for key, col in self.annotations.items()
929 }
930
931 # 2. Rename the alias in the internal table/alias datastructures.
932 for old_alias, new_alias in change_map.items():
933 if old_alias not in self.alias_map:
934 continue
935 alias_data = self.alias_map[old_alias].relabeled_clone(change_map)
936 self.alias_map[new_alias] = alias_data
937 self.alias_refcount[new_alias] = self.alias_refcount[old_alias]
938 del self.alias_refcount[old_alias]
939 del self.alias_map[old_alias]
940
941 table_aliases = self.table_map[alias_data.table_name]
942 for pos, alias in enumerate(table_aliases):
943 if alias == old_alias:
944 table_aliases[pos] = new_alias
945 break
946 self.external_aliases = {
947 # Table is aliased or it's being changed and thus is aliased.
948 change_map.get(alias, alias): (aliased or alias in change_map)
949 for alias, aliased in self.external_aliases.items()
950 }
951
952 def bump_prefix(
953 self, other_query: Query, exclude: set[str] | dict[str, str] | None = None
954 ) -> None:
955 """
956 Change the alias prefix to the next letter in the alphabet in a way
957 that the other query's aliases and this query's aliases will not
958 conflict. Even tables that previously had no alias will get an alias
959 after this call. To prevent changing aliases use the exclude parameter.
960 """
961
962 def prefix_gen() -> TypingIterator[str]:
963 """
964 Generate a sequence of characters in alphabetical order:
965 -> 'A', 'B', 'C', ...
966
967 When the alphabet is finished, the sequence will continue with the
968 Cartesian product:
969 -> 'AA', 'AB', 'AC', ...
970 """
971 alphabet = ascii_uppercase
972 prefix = chr(ord(self.alias_prefix) + 1)
973 yield prefix
974 for n in count(1):
975 seq = alphabet[alphabet.index(prefix) :] if prefix else alphabet
976 for s in product(seq, repeat=n):
977 yield "".join(s)
978 prefix = None
979
980 if self.alias_prefix != other_query.alias_prefix:
981 # No clashes between self and outer query should be possible.
982 return
983
984 # Explicitly avoid infinite loop. The constant divider is based on how
985 # much depth recursive subquery references add to the stack. This value
986 # might need to be adjusted when adding or removing function calls from
987 # the code path in charge of performing these operations.
988 local_recursion_limit = sys.getrecursionlimit() // 16
989 for pos, prefix in enumerate(prefix_gen()):
990 if prefix not in self.subq_aliases:
991 self.alias_prefix = prefix
992 break
993 if pos > local_recursion_limit:
994 raise RecursionError(
995 "Maximum recursion depth exceeded: too many subqueries."
996 )
997 self.subq_aliases = self.subq_aliases.union([self.alias_prefix])
998 other_query.subq_aliases = other_query.subq_aliases.union(self.subq_aliases)
999 if exclude is None:
1000 exclude = {}
1001 self.change_aliases(
1002 {
1003 alias: "%s%d" % (self.alias_prefix, pos) # noqa: UP031
1004 for pos, alias in enumerate(self.alias_map)
1005 if alias not in exclude
1006 }
1007 )
1008
1009 def get_initial_alias(self) -> str | None:
1010 """
1011 Return the first alias for this query, after increasing its reference
1012 count.
1013 """
1014 if self.alias_map:
1015 alias = self.base_table
1016 self.ref_alias(alias)
1017 elif self.model:
1018 alias = self.join(
1019 self.base_table_class(self.model.model_options.db_table, None)
1020 )
1021 else:
1022 alias = None
1023 return alias
1024
1025 def count_active_tables(self) -> int:
1026 """
1027 Return the number of tables in this query with a non-zero reference
1028 count. After execution, the reference counts are zeroed, so tables
1029 added in compiler will not be seen by this method.
1030 """
1031 return len([1 for count in self.alias_refcount.values() if count])
1032
1033 def join(
1034 self,
1035 join: BaseTable | Join,
1036 reuse: set[str] | None = None,
1037 reuse_with_filtered_relation: bool = False,
1038 ) -> str:
1039 """
1040 Return an alias for the 'join', either reusing an existing alias for
1041 that join or creating a new one. 'join' is either a base_table_class or
1042 join_class.
1043
1044 The 'reuse' parameter can be either None which means all joins are
1045 reusable, or it can be a set containing the aliases that can be reused.
1046
1047 The 'reuse_with_filtered_relation' parameter is used when computing
1048 FilteredRelation instances.
1049
1050 A join is always created as LOUTER if the lhs alias is LOUTER to make
1051 sure chains like t1 LOUTER t2 INNER t3 aren't generated. All new
1052 joins are created as LOUTER if the join is nullable.
1053 """
1054 if reuse_with_filtered_relation and reuse:
1055 reuse_aliases = [
1056 a for a, j in self.alias_map.items() if a in reuse and j.equals(join)
1057 ]
1058 else:
1059 reuse_aliases = [
1060 a
1061 for a, j in self.alias_map.items()
1062 if (reuse is None or a in reuse) and j == join
1063 ]
1064 if reuse_aliases:
1065 if join.table_alias in reuse_aliases:
1066 reuse_alias = join.table_alias
1067 else:
1068 # Reuse the most recent alias of the joined table
1069 # (a many-to-many relation may be joined multiple times).
1070 reuse_alias = reuse_aliases[-1]
1071 self.ref_alias(reuse_alias)
1072 return reuse_alias
1073
1074 # No reuse is possible, so we need a new alias.
1075 alias, _ = self.table_alias(
1076 join.table_name, create=True, filtered_relation=join.filtered_relation
1077 )
1078 if join.join_type:
1079 if self.alias_map[join.parent_alias].join_type == LOUTER or join.nullable: # type: ignore[attr-defined]
1080 join_type = LOUTER
1081 else:
1082 join_type = INNER
1083 join.join_type = join_type
1084 join.table_alias = alias
1085 self.alias_map[alias] = join
1086 return alias
1087
1088 def check_alias(self, alias: str) -> None:
1089 if FORBIDDEN_ALIAS_PATTERN.search(alias):
1090 raise ValueError(
1091 "Column aliases cannot contain whitespace characters, quotation marks, "
1092 "semicolons, or SQL comments."
1093 )
1094
1095 def add_annotation(
1096 self, annotation: BaseExpression, alias: str, select: bool = True
1097 ) -> None:
1098 """Add a single annotation expression to the Query."""
1099 self.check_alias(alias)
1100 annotation = annotation.resolve_expression(self, allow_joins=True, reuse=None)
1101 if select:
1102 self.append_annotation_mask([alias])
1103 else:
1104 self.set_annotation_mask(set(self.annotation_select).difference({alias}))
1105 self.annotations[alias] = annotation
1106
1107 def resolve_expression(self, query: Query, *args: Any, **kwargs: Any) -> Self:
1108 clone = self.clone()
1109 # Subqueries need to use a different set of aliases than the outer query.
1110 clone.bump_prefix(query)
1111 clone.subquery = True
1112 clone.where.resolve_expression(query, *args, **kwargs)
1113 # Resolve combined queries.
1114 if clone.combinator:
1115 clone.combined_queries = tuple(
1116 [
1117 combined_query.resolve_expression(query, *args, **kwargs)
1118 for combined_query in clone.combined_queries
1119 ]
1120 )
1121 for key, value in clone.annotations.items():
1122 resolved = value.resolve_expression(query, *args, **kwargs)
1123 if hasattr(resolved, "external_aliases"):
1124 resolved.external_aliases.update(clone.external_aliases)
1125 clone.annotations[key] = resolved
1126 # Outer query's aliases are considered external.
1127 for alias, table in query.alias_map.items():
1128 clone.external_aliases[alias] = (
1129 isinstance(table, Join)
1130 and table.join_field.related_model.model_options.db_table != alias
1131 ) or (
1132 isinstance(table, BaseTable) and table.table_name != table.table_alias
1133 )
1134 return clone
1135
1136 def get_external_cols(self) -> list[Col]:
1137 exprs = chain(self.annotations.values(), self.where.children)
1138 return [
1139 col
1140 for col in self._gen_cols(exprs, include_external=True)
1141 if col.alias in self.external_aliases
1142 ]
1143
1144 def get_group_by_cols(
1145 self, wrapper: BaseExpression | None = None
1146 ) -> list[Col] | list[BaseExpression]:
1147 # If wrapper is referenced by an alias for an explicit GROUP BY through
1148 # values() a reference to this expression and not the self must be
1149 # returned to ensure external column references are not grouped against
1150 # as well.
1151 external_cols = self.get_external_cols()
1152 if any(col.possibly_multivalued for col in external_cols):
1153 return [wrapper or self]
1154 return external_cols
1155
1156 def as_sql(
1157 self, compiler: SQLCompiler, connection: BaseDatabaseWrapper
1158 ) -> tuple[str, tuple[Any, ...]]:
1159 # Some backends (e.g. Oracle) raise an error when a subquery contains
1160 # unnecessary ORDER BY clause.
1161 if (
1162 self.subquery
1163 and not db_connection.features.ignores_unnecessary_order_by_in_subqueries
1164 ):
1165 self.clear_ordering(force=False)
1166 for query in self.combined_queries:
1167 query.clear_ordering(force=False)
1168 sql, params = self.get_compiler().as_sql()
1169 if self.subquery:
1170 sql = f"({sql})"
1171 return sql, params
1172
1173 def resolve_lookup_value(
1174 self, value: Any, can_reuse: set[str] | None, allow_joins: bool
1175 ) -> Any:
1176 if hasattr(value, "resolve_expression"):
1177 value = value.resolve_expression(
1178 self,
1179 reuse=can_reuse,
1180 allow_joins=allow_joins,
1181 )
1182 elif isinstance(value, list | tuple):
1183 # The items of the iterable may be expressions and therefore need
1184 # to be resolved independently.
1185 values = (
1186 self.resolve_lookup_value(sub_value, can_reuse, allow_joins)
1187 for sub_value in value
1188 )
1189 type_ = type(value)
1190 if hasattr(type_, "_make"): # namedtuple
1191 return type_(*values)
1192 return type_(values)
1193 return value
1194
1195 def solve_lookup_type(
1196 self, lookup: str, summarize: bool = False
1197 ) -> tuple[
1198 list[str] | tuple[str, ...], tuple[str, ...], BaseExpression | Literal[False]
1199 ]:
1200 """
1201 Solve the lookup type from the lookup (e.g.: 'foobar__id__icontains').
1202 """
1203 lookup_splitted = lookup.split(LOOKUP_SEP)
1204 if self.annotations:
1205 annotation, expression_lookups = refs_expression(
1206 lookup_splitted, self.annotations
1207 )
1208 if annotation:
1209 expression = self.annotations[annotation]
1210 if summarize:
1211 expression = Ref(annotation, expression)
1212 return expression_lookups, (), expression
1213 _, field, _, lookup_parts = self.names_to_path(
1214 lookup_splitted, self.get_model_meta()
1215 )
1216 field_parts = lookup_splitted[0 : len(lookup_splitted) - len(lookup_parts)]
1217 if len(lookup_parts) > 1 and not field_parts:
1218 raise FieldError(
1219 f'Invalid lookup "{lookup}" for model {self.get_model_meta().model.__name__}".'
1220 )
1221 return lookup_parts, field_parts, False # type: ignore[return-value]
1222
1223 def check_query_object_type(self, value: Any, meta: Meta, field: Field) -> None:
1224 """
1225 Check whether the object passed while querying is of the correct type.
1226 If not, raise a ValueError specifying the wrong object.
1227 """
1228 if hasattr(value, "_model_meta"):
1229 if not check_rel_lookup_compatibility(value._model_meta.model, meta, field):
1230 raise ValueError(
1231 f'Cannot query "{value}": Must be "{meta.model.model_options.object_name}" instance.'
1232 )
1233
1234 def check_related_objects(self, field: Field, value: Any, meta: Meta) -> None:
1235 """Check the type of object passed to query relations."""
1236 if field.is_relation:
1237 # Check that the field and the queryset use the same model in a
1238 # query like .filter(author=Author.query.all()). For example, the
1239 # meta would be Author's (from the author field) and value.model
1240 # would be Author.query.all() queryset's .model (Author also).
1241 # The field is the related field on the lhs side.
1242 if (
1243 isinstance(value, Query)
1244 and not value.has_select_fields
1245 and not check_rel_lookup_compatibility(value.model, meta, field)
1246 ):
1247 raise ValueError(
1248 f'Cannot use QuerySet for "{value.model.model_options.object_name}": Use a QuerySet for "{meta.model.model_options.object_name}".'
1249 )
1250 elif hasattr(value, "_model_meta"):
1251 self.check_query_object_type(value, meta, field)
1252 elif hasattr(value, "__iter__"):
1253 for v in value:
1254 self.check_query_object_type(v, meta, field)
1255
1256 def check_filterable(self, expression: Any) -> None:
1257 """Raise an error if expression cannot be used in a WHERE clause."""
1258 if hasattr(expression, "resolve_expression") and not getattr(
1259 expression, "filterable", True
1260 ):
1261 raise NotSupportedError(
1262 expression.__class__.__name__ + " is disallowed in the filter clause."
1263 )
1264 if hasattr(expression, "get_source_expressions"):
1265 for expr in expression.get_source_expressions():
1266 self.check_filterable(expr)
1267
1268 def build_lookup(
1269 self, lookups: list[str], lhs: BaseExpression | MultiColSource, rhs: Any
1270 ) -> Lookup | None:
1271 """
1272 Try to extract transforms and lookup from given lhs.
1273
1274 The lhs value is something that works like SQLExpression.
1275 The rhs value is what the lookup is going to compare against.
1276 The lookups is a list of names to extract using get_lookup()
1277 and get_transform().
1278 """
1279 # __exact is the default lookup if one isn't given.
1280 *transforms, lookup_name = lookups or ["exact"]
1281 for name in transforms:
1282 lhs = self.try_transform(lhs, name)
1283 # First try get_lookup() so that the lookup takes precedence if the lhs
1284 # supports both transform and lookup for the name.
1285 lookup_class = lhs.get_lookup(lookup_name)
1286 if not lookup_class:
1287 # A lookup wasn't found. Try to interpret the name as a transform
1288 # and do an Exact lookup against it.
1289 lhs = self.try_transform(lhs, lookup_name)
1290 lookup_name = "exact"
1291 lookup_class = lhs.get_lookup(lookup_name)
1292 if not lookup_class:
1293 return
1294
1295 lookup = lookup_class(lhs, rhs)
1296 # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
1297 # uses of None as a query value unless the lookup supports it.
1298 if lookup.rhs is None and not lookup.can_use_none_as_rhs:
1299 if lookup_name not in ("exact", "iexact"):
1300 raise ValueError("Cannot use None as a query value")
1301 return lhs.get_lookup("isnull")(lhs, True) # type: ignore[return-value]
1302
1303 return lookup
1304
1305 def try_transform(
1306 self, lhs: BaseExpression | MultiColSource, name: str
1307 ) -> BaseExpression | MultiColSource:
1308 """
1309 Helper method for build_lookup(). Try to fetch and initialize
1310 a transform for name parameter from lhs.
1311 """
1312 transform_class = lhs.get_transform(name) # type: ignore[union-attr]
1313 if transform_class:
1314 return transform_class(lhs)
1315 else:
1316 output_field = lhs.output_field.__class__
1317 suggested_lookups = difflib.get_close_matches(
1318 name, output_field.get_lookups()
1319 )
1320 if suggested_lookups:
1321 suggestion = ", perhaps you meant {}?".format(
1322 " or ".join(suggested_lookups)
1323 )
1324 else:
1325 suggestion = "."
1326 raise FieldError(
1327 f"Unsupported lookup '{name}' for {output_field.__name__} or join on the field not "
1328 f"permitted{suggestion}"
1329 )
1330
1331 def build_filter(
1332 self,
1333 filter_expr: tuple[str, Any] | Q | BaseExpression,
1334 branch_negated: bool = False,
1335 current_negated: bool = False,
1336 can_reuse: set[str] | None = None,
1337 allow_joins: bool = True,
1338 split_subq: bool = True,
1339 reuse_with_filtered_relation: bool = False,
1340 check_filterable: bool = True,
1341 summarize: bool = False,
1342 ) -> tuple[WhereNode, set[str] | tuple[()]]:
1343 """
1344 Build a WhereNode for a single filter clause but don't add it
1345 to this Query. Query.add_q() will then add this filter to the where
1346 Node.
1347
1348 The 'branch_negated' tells us if the current branch contains any
1349 negations. This will be used to determine if subqueries are needed.
1350
1351 The 'current_negated' is used to determine if the current filter is
1352 negated or not and this will be used to determine if IS NULL filtering
1353 is needed.
1354
1355 The difference between current_negated and branch_negated is that
1356 branch_negated is set on first negation, but current_negated is
1357 flipped for each negation.
1358
1359 Note that add_filter will not do any negating itself, that is done
1360 upper in the code by add_q().
1361
1362 The 'can_reuse' is a set of reusable joins for multijoins.
1363
1364 If 'reuse_with_filtered_relation' is True, then only joins in can_reuse
1365 will be reused.
1366
1367 The method will create a filter clause that can be added to the current
1368 query. However, if the filter isn't added to the query then the caller
1369 is responsible for unreffing the joins used.
1370 """
1371 if isinstance(filter_expr, dict):
1372 raise FieldError("Cannot parse keyword query as dict")
1373 if isinstance(filter_expr, Q):
1374 return self._add_q(
1375 filter_expr,
1376 branch_negated=branch_negated,
1377 current_negated=current_negated,
1378 used_aliases=can_reuse,
1379 allow_joins=allow_joins,
1380 split_subq=split_subq,
1381 check_filterable=check_filterable,
1382 summarize=summarize,
1383 )
1384 if hasattr(filter_expr, "resolve_expression"):
1385 if not getattr(filter_expr, "conditional", False):
1386 raise TypeError("Cannot filter against a non-conditional expression.")
1387 condition = filter_expr.resolve_expression(
1388 self, allow_joins=allow_joins, summarize=summarize
1389 ) # type: ignore[attr-defined]
1390 if not isinstance(condition, Lookup):
1391 condition = self.build_lookup(["exact"], condition, True)
1392 return WhereNode([condition], connector=AND), [] # type: ignore[return-value]
1393 arg, value = filter_expr
1394 if not arg:
1395 raise FieldError(f"Cannot parse keyword query {arg!r}")
1396 lookups, parts, reffed_expression = self.solve_lookup_type(arg, summarize)
1397
1398 if check_filterable:
1399 self.check_filterable(reffed_expression)
1400
1401 if not allow_joins and len(parts) > 1:
1402 raise FieldError("Joined field references are not permitted in this query")
1403
1404 pre_joins = self.alias_refcount.copy()
1405 value = self.resolve_lookup_value(value, can_reuse, allow_joins)
1406 used_joins = {
1407 k for k, v in self.alias_refcount.items() if v > pre_joins.get(k, 0)
1408 }
1409
1410 if check_filterable:
1411 self.check_filterable(value)
1412
1413 if reffed_expression:
1414 condition = self.build_lookup(list(lookups), reffed_expression, value)
1415 return WhereNode([condition], connector=AND), [] # type: ignore[return-value]
1416
1417 meta = self.get_model_meta()
1418 alias = self.get_initial_alias()
1419 assert alias is not None
1420 allow_many = not branch_negated or not split_subq
1421
1422 try:
1423 join_info = self.setup_joins(
1424 list(parts),
1425 meta,
1426 alias,
1427 can_reuse=can_reuse,
1428 allow_many=allow_many,
1429 reuse_with_filtered_relation=reuse_with_filtered_relation,
1430 )
1431
1432 # Prevent iterator from being consumed by check_related_objects()
1433 if isinstance(value, Iterator):
1434 value = list(value)
1435 self.check_related_objects(join_info.final_field, value, join_info.meta)
1436
1437 # split_exclude() needs to know which joins were generated for the
1438 # lookup parts
1439 self._lookup_joins = join_info.joins
1440 except MultiJoin as e:
1441 return self.split_exclude(
1442 filter_expr,
1443 can_reuse or set(),
1444 e.names_with_path, # type: ignore[invalid-argument-type]
1445 )
1446
1447 # Update used_joins before trimming since they are reused to determine
1448 # which joins could be later promoted to INNER.
1449 used_joins.update(join_info.joins)
1450 targets, alias, join_list = self.trim_joins(
1451 join_info.targets, join_info.joins, join_info.path
1452 )
1453 if can_reuse is not None:
1454 can_reuse.update(join_list)
1455
1456 if join_info.final_field.is_relation:
1457 if len(targets) == 1:
1458 col = self._get_col(targets[0], join_info.final_field, alias)
1459 else:
1460 col = MultiColSource(
1461 alias, targets, join_info.targets, join_info.final_field
1462 )
1463 else:
1464 col = self._get_col(targets[0], join_info.final_field, alias)
1465
1466 condition = self.build_lookup(list(lookups), col, value)
1467 assert condition is not None
1468 lookup_type = condition.lookup_name
1469 clause = WhereNode([condition], connector=AND)
1470
1471 require_outer = (
1472 lookup_type == "isnull" and condition.rhs is True and not current_negated
1473 )
1474 if (
1475 current_negated
1476 and (lookup_type != "isnull" or condition.rhs is False)
1477 and condition.rhs is not None
1478 ):
1479 require_outer = True
1480 if lookup_type != "isnull":
1481 # The condition added here will be SQL like this:
1482 # NOT (col IS NOT NULL), where the first NOT is added in
1483 # upper layers of code. The reason for addition is that if col
1484 # is null, then col != someval will result in SQL "unknown"
1485 # which isn't the same as in Python. The Python None handling
1486 # is wanted, and it can be gotten by
1487 # (col IS NULL OR col != someval)
1488 # <=>
1489 # NOT (col IS NOT NULL AND col = someval).
1490 if (
1491 self.is_nullable(targets[0])
1492 or self.alias_map[join_list[-1]].join_type == LOUTER
1493 ):
1494 lookup_class = targets[0].get_lookup("isnull")
1495 assert lookup_class is not None
1496 col = self._get_col(targets[0], join_info.targets[0], alias)
1497 clause.add(lookup_class(col, False), AND)
1498 # If someval is a nullable column, someval IS NOT NULL is
1499 # added.
1500 if isinstance(value, Col) and self.is_nullable(value.target):
1501 lookup_class = value.target.get_lookup("isnull")
1502 assert lookup_class is not None
1503 clause.add(lookup_class(value, False), AND)
1504 return clause, used_joins if not require_outer else ()
1505
1506 def add_filter(self, filter_lhs: str, filter_rhs: Any) -> None:
1507 self.add_q(Q((filter_lhs, filter_rhs)))
1508
1509 def add_q(self, q_object: Q) -> None: # type: ignore[unsupported-operator]
1510 """
1511 A preprocessor for the internal _add_q(). Responsible for doing final
1512 join promotion.
1513 """
1514 # For join promotion this case is doing an AND for the added q_object
1515 # and existing conditions. So, any existing inner join forces the join
1516 # type to remain inner. Existing outer joins can however be demoted.
1517 # (Consider case where rel_a is LOUTER and rel_a__col=1 is added - if
1518 # rel_a doesn't produce any rows, then the whole condition must fail.
1519 # So, demotion is OK.
1520 existing_inner = {
1521 a for a in self.alias_map if self.alias_map[a].join_type == INNER
1522 }
1523 clause, _ = self._add_q(q_object, self.used_aliases)
1524 if clause:
1525 self.where.add(clause, AND)
1526 self.demote_joins(existing_inner)
1527
1528 def build_where(
1529 self, filter_expr: tuple[str, Any] | Q | BaseExpression
1530 ) -> WhereNode:
1531 return self.build_filter(filter_expr, allow_joins=False)[0]
1532
1533 def clear_where(self) -> None:
1534 self.where = WhereNode()
1535
1536 def _add_q(
1537 self,
1538 q_object: Q,
1539 used_aliases: set[str] | None,
1540 branch_negated: bool = False,
1541 current_negated: bool = False,
1542 allow_joins: bool = True,
1543 split_subq: bool = True,
1544 check_filterable: bool = True,
1545 summarize: bool = False,
1546 ) -> tuple[WhereNode, set[str] | tuple[()]]: # type: ignore[unsupported-operator]
1547 """Add a Q-object to the current filter."""
1548 connector = q_object.connector
1549 current_negated ^= q_object.negated
1550 branch_negated = branch_negated or q_object.negated
1551 target_clause = WhereNode(connector=connector, negated=q_object.negated)
1552 joinpromoter = JoinPromoter(
1553 q_object.connector, len(q_object.children), current_negated
1554 )
1555 for child in q_object.children:
1556 child_clause, needed_inner = self.build_filter(
1557 child,
1558 can_reuse=used_aliases,
1559 branch_negated=branch_negated,
1560 current_negated=current_negated,
1561 allow_joins=allow_joins,
1562 split_subq=split_subq,
1563 check_filterable=check_filterable,
1564 summarize=summarize,
1565 )
1566 joinpromoter.add_votes(needed_inner)
1567 if child_clause:
1568 target_clause.add(child_clause, connector)
1569 needed_inner = joinpromoter.update_join_types(self)
1570 return target_clause, needed_inner
1571
1572 def build_filtered_relation_q(
1573 self,
1574 q_object: Q,
1575 reuse: set[str],
1576 branch_negated: bool = False,
1577 current_negated: bool = False,
1578 ) -> WhereNode: # type: ignore[unsupported-operator]
1579 """Add a FilteredRelation object to the current filter."""
1580 connector = q_object.connector
1581 current_negated ^= q_object.negated
1582 branch_negated = branch_negated or q_object.negated
1583 target_clause = WhereNode(connector=connector, negated=q_object.negated)
1584 for child in q_object.children:
1585 if isinstance(child, Node):
1586 child_clause = self.build_filtered_relation_q(
1587 child,
1588 reuse=reuse,
1589 branch_negated=branch_negated,
1590 current_negated=current_negated,
1591 )
1592 else:
1593 child_clause, _ = self.build_filter(
1594 child,
1595 can_reuse=reuse,
1596 branch_negated=branch_negated,
1597 current_negated=current_negated,
1598 allow_joins=True,
1599 split_subq=False,
1600 reuse_with_filtered_relation=True,
1601 )
1602 target_clause.add(child_clause, connector)
1603 return target_clause
1604
1605 def add_filtered_relation(self, filtered_relation: Any, alias: str) -> None:
1606 filtered_relation.alias = alias
1607 lookups = dict(get_children_from_q(filtered_relation.condition))
1608 relation_lookup_parts, relation_field_parts, _ = self.solve_lookup_type(
1609 filtered_relation.relation_name
1610 )
1611 if relation_lookup_parts:
1612 raise ValueError(
1613 "FilteredRelation's relation_name cannot contain lookups "
1614 f"(got {filtered_relation.relation_name!r})."
1615 )
1616 for lookup in chain(lookups):
1617 lookup_parts, lookup_field_parts, _ = self.solve_lookup_type(lookup)
1618 shift = 2 if not lookup_parts else 1
1619 lookup_field_path = lookup_field_parts[:-shift]
1620 for idx, lookup_field_part in enumerate(lookup_field_path):
1621 if len(relation_field_parts) > idx:
1622 if relation_field_parts[idx] != lookup_field_part:
1623 raise ValueError(
1624 "FilteredRelation's condition doesn't support "
1625 f"relations outside the {filtered_relation.relation_name!r} (got {lookup!r})."
1626 )
1627 else:
1628 raise ValueError(
1629 "FilteredRelation's condition doesn't support nested "
1630 f"relations deeper than the relation_name (got {lookup!r} for "
1631 f"{filtered_relation.relation_name!r})."
1632 )
1633 self._filtered_relations[filtered_relation.alias] = filtered_relation
1634
1635 def names_to_path(
1636 self,
1637 names: list[str],
1638 meta: Meta,
1639 allow_many: bool = True,
1640 fail_on_missing: bool = False,
1641 ) -> tuple[list[Any], Field, tuple[Field, ...], list[str]]:
1642 """
1643 Walk the list of names and turns them into PathInfo tuples. A single
1644 name in 'names' can generate multiple PathInfos (m2m, for example).
1645
1646 'names' is the path of names to travel, 'meta' is the Meta we
1647 start the name resolving from, 'allow_many' is as for setup_joins().
1648 If fail_on_missing is set to True, then a name that can't be resolved
1649 will generate a FieldError.
1650
1651 Return a list of PathInfo tuples. In addition return the final field
1652 (the last used join field) and target (which is a field guaranteed to
1653 contain the same value as the final field). Finally, return those names
1654 that weren't found (which are likely transforms and the final lookup).
1655 """
1656 path, names_with_path = [], []
1657 for pos, name in enumerate(names):
1658 cur_names_with_path = (name, [])
1659
1660 field = None
1661 filtered_relation = None
1662 try:
1663 if meta is None:
1664 raise FieldDoesNotExist
1665 field = meta.get_field(name)
1666 except FieldDoesNotExist:
1667 if name in self.annotation_select:
1668 field = self.annotation_select[name].output_field
1669 elif name in self._filtered_relations and pos == 0:
1670 filtered_relation = self._filtered_relations[name]
1671 if LOOKUP_SEP in filtered_relation.relation_name:
1672 parts = filtered_relation.relation_name.split(LOOKUP_SEP)
1673 filtered_relation_path, field, _, _ = self.names_to_path(
1674 parts,
1675 meta,
1676 allow_many,
1677 fail_on_missing,
1678 )
1679 path.extend(filtered_relation_path[:-1])
1680 else:
1681 field = meta.get_field(filtered_relation.relation_name) # type: ignore[attr-defined]
1682 if field is not None:
1683 # Fields that contain one-to-many relations with a generic
1684 # model (like a GenericForeignKey) cannot generate reverse
1685 # relations and therefore cannot be used for reverse querying.
1686 if field.is_relation and not field.related_model:
1687 raise FieldError(
1688 f"Field {name!r} does not generate an automatic reverse "
1689 "relation and therefore cannot be used for reverse "
1690 "querying. If it is a GenericForeignKey, consider "
1691 "adding a GenericRelation."
1692 )
1693 else:
1694 # We didn't find the current field, so move position back
1695 # one step.
1696 pos -= 1
1697 if pos == -1 or fail_on_missing:
1698 available = sorted(
1699 [
1700 *get_field_names_from_opts(meta),
1701 *self.annotation_select,
1702 *self._filtered_relations,
1703 ]
1704 )
1705 raise FieldError(
1706 "Cannot resolve keyword '{}' into field. "
1707 "Choices are: {}".format(name, ", ".join(available))
1708 )
1709 break
1710
1711 if hasattr(field, "path_infos"):
1712 pathinfos: list[PathInfo]
1713 if filtered_relation:
1714 pathinfos = field.get_path_info(filtered_relation) # type: ignore[attr-defined]
1715 else:
1716 pathinfos = field.path_infos # type: ignore[attr-defined]
1717 if not allow_many:
1718 for inner_pos, p in enumerate(pathinfos):
1719 if p.m2m:
1720 cur_names_with_path[1].extend(pathinfos[0 : inner_pos + 1])
1721 names_with_path.append(cur_names_with_path)
1722 raise MultiJoin(pos + 1, names_with_path)
1723 last = pathinfos[-1]
1724 path.extend(pathinfos)
1725 final_field = last.join_field
1726 meta = last.to_meta
1727 targets = last.target_fields
1728 cur_names_with_path[1].extend(pathinfos)
1729 names_with_path.append(cur_names_with_path)
1730 else:
1731 # Local non-relational field.
1732 final_field = field
1733 targets = (field,)
1734 if fail_on_missing and pos + 1 != len(names):
1735 raise FieldError(
1736 f"Cannot resolve keyword {names[pos + 1]!r} into field. Join on '{name}'"
1737 " not permitted."
1738 )
1739 break
1740 return path, final_field, targets, names[pos + 1 :]
1741
1742 def setup_joins(
1743 self,
1744 names: list[str],
1745 meta: Meta,
1746 alias: str,
1747 can_reuse: set[str] | None = None,
1748 allow_many: bool = True,
1749 reuse_with_filtered_relation: bool = False,
1750 ) -> JoinInfo:
1751 """
1752 Compute the necessary table joins for the passage through the fields
1753 given in 'names'. 'meta' is the Meta for the current model
1754 (which gives the table we are starting from), 'alias' is the alias for
1755 the table to start the joining from.
1756
1757 The 'can_reuse' defines the reverse foreign key joins we can reuse. It
1758 can be None in which case all joins are reusable or a set of aliases
1759 that can be reused. Note that non-reverse foreign keys are always
1760 reusable when using setup_joins().
1761
1762 The 'reuse_with_filtered_relation' can be used to force 'can_reuse'
1763 parameter and force the relation on the given connections.
1764
1765 If 'allow_many' is False, then any reverse foreign key seen will
1766 generate a MultiJoin exception.
1767
1768 Return the final field involved in the joins, the target field (used
1769 for any 'where' constraint), the final 'opts' value, the joins, the
1770 field path traveled to generate the joins, and a transform function
1771 that takes a field and alias and is equivalent to `field.get_col(alias)`
1772 in the simple case but wraps field transforms if they were included in
1773 names.
1774
1775 The target field is the field containing the concrete value. Final
1776 field can be something different, for example foreign key pointing to
1777 that value. Final field is needed for example in some value
1778 conversions (convert 'obj' in fk__id=obj to pk val using the foreign
1779 key field for example).
1780 """
1781 joins = [alias]
1782 # The transform can't be applied yet, as joins must be trimmed later.
1783 # To avoid making every caller of this method look up transforms
1784 # directly, compute transforms here and create a partial that converts
1785 # fields to the appropriate wrapped version.
1786
1787 def final_transformer(field: Field, alias: str | None) -> Col:
1788 if not self.alias_cols:
1789 alias = None
1790 return field.get_col(alias) # type: ignore[arg-type]
1791
1792 # Try resolving all the names as fields first. If there's an error,
1793 # treat trailing names as lookups until a field can be resolved.
1794 last_field_exception = None
1795 for pivot in range(len(names), 0, -1):
1796 try:
1797 path, final_field, targets, rest = self.names_to_path(
1798 names[:pivot],
1799 meta,
1800 allow_many,
1801 fail_on_missing=True,
1802 )
1803 except FieldError as exc:
1804 if pivot == 1:
1805 # The first item cannot be a lookup, so it's safe
1806 # to raise the field error here.
1807 raise
1808 else:
1809 last_field_exception = exc
1810 else:
1811 # The transforms are the remaining items that couldn't be
1812 # resolved into fields.
1813 transforms = names[pivot:]
1814 break
1815 for name in transforms:
1816
1817 def transform(
1818 field: Field, alias: str | None, *, name: str, previous: Any
1819 ) -> BaseExpression | MultiColSource:
1820 try:
1821 wrapped = previous(field, alias)
1822 return self.try_transform(wrapped, name)
1823 except FieldError:
1824 # FieldError is raised if the transform doesn't exist.
1825 if isinstance(final_field, Field) and last_field_exception:
1826 raise last_field_exception
1827 else:
1828 raise
1829
1830 final_transformer = functools.partial( # type: ignore[misc]
1831 transform, name=name, previous=final_transformer
1832 )
1833 final_transformer.has_transforms = True # type: ignore[attr-defined]
1834 # Then, add the path to the query's joins. Note that we can't trim
1835 # joins at this stage - we will need the information about join type
1836 # of the trimmed joins.
1837 for join in path:
1838 if join.filtered_relation:
1839 filtered_relation = join.filtered_relation.clone()
1840 table_alias = filtered_relation.alias
1841 else:
1842 filtered_relation = None
1843 table_alias = None
1844 meta = join.to_meta
1845 if join.direct:
1846 nullable = self.is_nullable(join.join_field)
1847 else:
1848 nullable = True
1849 connection = self.join_class(
1850 meta.model.model_options.db_table,
1851 alias,
1852 table_alias,
1853 INNER,
1854 join.join_field,
1855 nullable,
1856 filtered_relation=filtered_relation,
1857 )
1858 reuse = can_reuse if join.m2m or reuse_with_filtered_relation else None
1859 alias = self.join(
1860 connection,
1861 reuse=reuse,
1862 reuse_with_filtered_relation=reuse_with_filtered_relation,
1863 )
1864 joins.append(alias)
1865 if filtered_relation:
1866 filtered_relation.path = joins[:]
1867 return JoinInfo(final_field, targets, meta, joins, path, final_transformer)
1868
1869 def trim_joins(
1870 self, targets: tuple[Field, ...], joins: list[str], path: list[Any]
1871 ) -> tuple[tuple[Field, ...], str, list[str]]:
1872 """
1873 The 'target' parameter is the final field being joined to, 'joins'
1874 is the full list of join aliases. The 'path' contain the PathInfos
1875 used to create the joins.
1876
1877 Return the final target field and table alias and the new active
1878 joins.
1879
1880 Always trim any direct join if the target column is already in the
1881 previous table. Can't trim reverse joins as it's unknown if there's
1882 anything on the other side of the join.
1883 """
1884 joins = joins[:]
1885 for pos, info in enumerate(reversed(path)):
1886 if len(joins) == 1 or not info.direct:
1887 break
1888 if info.filtered_relation:
1889 break
1890 join_targets = {t.column for t in info.join_field.foreign_related_fields}
1891 cur_targets = {t.column for t in targets}
1892 if not cur_targets.issubset(join_targets):
1893 break
1894 targets_dict = {
1895 r[1].column: r[0]
1896 for r in info.join_field.related_fields
1897 if r[1].column in cur_targets
1898 }
1899 targets = tuple(targets_dict[t.column] for t in targets)
1900 self.unref_alias(joins.pop())
1901 return targets, joins[-1], joins
1902
1903 @classmethod
1904 def _gen_cols(
1905 cls,
1906 exprs: Iterable[Any],
1907 include_external: bool = False,
1908 resolve_refs: bool = True,
1909 ) -> TypingIterator[Col]:
1910 for expr in exprs:
1911 if isinstance(expr, Col):
1912 yield expr
1913 elif include_external and callable(
1914 getattr(expr, "get_external_cols", None)
1915 ):
1916 yield from expr.get_external_cols()
1917 elif hasattr(expr, "get_source_expressions"):
1918 if not resolve_refs and isinstance(expr, Ref):
1919 continue
1920 yield from cls._gen_cols(
1921 expr.get_source_expressions(),
1922 include_external=include_external,
1923 resolve_refs=resolve_refs,
1924 )
1925
1926 @classmethod
1927 def _gen_col_aliases(cls, exprs: Iterable[Any]) -> TypingIterator[str]:
1928 yield from (expr.alias for expr in cls._gen_cols(exprs))
1929
1930 def resolve_ref(
1931 self,
1932 name: str,
1933 allow_joins: bool = True,
1934 reuse: set[str] | None = None,
1935 summarize: bool = False,
1936 ) -> BaseExpression | MultiColSource:
1937 annotation = self.annotations.get(name)
1938 if annotation is not None:
1939 if not allow_joins:
1940 for alias in self._gen_col_aliases([annotation]):
1941 if isinstance(self.alias_map[alias], Join):
1942 raise FieldError(
1943 "Joined field references are not permitted in this query"
1944 )
1945 if summarize:
1946 # Summarize currently means we are doing an aggregate() query
1947 # which is executed as a wrapped subquery if any of the
1948 # aggregate() elements reference an existing annotation. In
1949 # that case we need to return a Ref to the subquery's annotation.
1950 if name not in self.annotation_select:
1951 raise FieldError(
1952 f"Cannot aggregate over the '{name}' alias. Use annotate() "
1953 "to promote it."
1954 )
1955 return Ref(name, self.annotation_select[name])
1956 else:
1957 return annotation
1958 else:
1959 field_list = name.split(LOOKUP_SEP)
1960 annotation = self.annotations.get(field_list[0])
1961 if annotation is not None:
1962 for transform in field_list[1:]:
1963 annotation = self.try_transform(annotation, transform)
1964 return annotation
1965 initial_alias = self.get_initial_alias()
1966 assert initial_alias is not None
1967 join_info = self.setup_joins(
1968 field_list,
1969 self.get_model_meta(),
1970 initial_alias,
1971 can_reuse=reuse,
1972 )
1973 targets, final_alias, join_list = self.trim_joins(
1974 join_info.targets, join_info.joins, join_info.path
1975 )
1976 if not allow_joins and len(join_list) > 1:
1977 raise FieldError(
1978 "Joined field references are not permitted in this query"
1979 )
1980 if len(targets) > 1:
1981 raise FieldError(
1982 "Referencing multicolumn fields with F() objects isn't supported"
1983 )
1984 # Verify that the last lookup in name is a field or a transform:
1985 # transform_function() raises FieldError if not.
1986 transform = join_info.transform_function(targets[0], final_alias)
1987 if reuse is not None:
1988 reuse.update(join_list)
1989 return transform
1990
1991 def split_exclude(
1992 self,
1993 filter_expr: tuple[str, Any],
1994 can_reuse: set[str],
1995 names_with_path: list[tuple[str, list[Any]]],
1996 ) -> tuple[WhereNode, set[str] | tuple[()]]:
1997 """
1998 When doing an exclude against any kind of N-to-many relation, we need
1999 to use a subquery. This method constructs the nested query, given the
2000 original exclude filter (filter_expr) and the portion up to the first
2001 N-to-many relation field.
2002
2003 For example, if the origin filter is ~Q(child__name='foo'), filter_expr
2004 is ('child__name', 'foo') and can_reuse is a set of joins usable for
2005 filters in the original query.
2006
2007 We will turn this into equivalent of:
2008 WHERE NOT EXISTS(
2009 SELECT 1
2010 FROM child
2011 WHERE name = 'foo' AND child.parent_id = parent.id
2012 LIMIT 1
2013 )
2014 """
2015 # Generate the inner query.
2016 query = self.__class__(self.model)
2017 query._filtered_relations = self._filtered_relations
2018 filter_lhs, filter_rhs = filter_expr
2019 if isinstance(filter_rhs, OuterRef):
2020 filter_rhs = OuterRef(filter_rhs)
2021 elif isinstance(filter_rhs, F):
2022 filter_rhs = OuterRef(filter_rhs.name)
2023 query.add_filter(filter_lhs, filter_rhs)
2024 query.clear_ordering(force=True)
2025 # Try to have as simple as possible subquery -> trim leading joins from
2026 # the subquery.
2027 trimmed_prefix, contains_louter = query.trim_start(names_with_path)
2028
2029 col = query.select[0]
2030 select_field = col.target
2031 alias = col.alias
2032 if alias in can_reuse:
2033 id_field = select_field.model._model_meta.get_field("id")
2034 # Need to add a restriction so that outer query's filters are in effect for
2035 # the subquery, too.
2036 query.bump_prefix(self)
2037 lookup_class = select_field.get_lookup("exact")
2038 # Note that the query.select[0].alias is different from alias
2039 # due to bump_prefix above.
2040 lookup = lookup_class(
2041 id_field.get_col(query.select[0].alias), id_field.get_col(alias)
2042 )
2043 query.where.add(lookup, AND)
2044 query.external_aliases[alias] = True
2045
2046 lookup_class = select_field.get_lookup("exact")
2047 lookup = lookup_class(col, ResolvedOuterRef(trimmed_prefix))
2048 query.where.add(lookup, AND)
2049 condition, needed_inner = self.build_filter(Exists(query))
2050
2051 if contains_louter:
2052 or_null_condition, _ = self.build_filter(
2053 (f"{trimmed_prefix}__isnull", True),
2054 current_negated=True,
2055 branch_negated=True,
2056 can_reuse=can_reuse,
2057 )
2058 condition.add(or_null_condition, OR)
2059 # Note that the end result will be:
2060 # (outercol NOT IN innerq AND outercol IS NOT NULL) OR outercol IS NULL.
2061 # This might look crazy but due to how IN works, this seems to be
2062 # correct. If the IS NOT NULL check is removed then outercol NOT
2063 # IN will return UNKNOWN. If the IS NULL check is removed, then if
2064 # outercol IS NULL we will not match the row.
2065 return condition, needed_inner
2066
2067 def set_empty(self) -> None:
2068 self.where.add(NothingNode(), AND)
2069 for query in self.combined_queries:
2070 query.set_empty()
2071
2072 def is_empty(self) -> bool:
2073 return any(isinstance(c, NothingNode) for c in self.where.children)
2074
2075 def set_limits(self, low: int | None = None, high: int | None = None) -> None:
2076 """
2077 Adjust the limits on the rows retrieved. Use low/high to set these,
2078 as it makes it more Pythonic to read and write. When the SQL query is
2079 created, convert them to the appropriate offset and limit values.
2080
2081 Apply any limits passed in here to the existing constraints. Add low
2082 to the current low value and clamp both to any existing high value.
2083 """
2084 if high is not None:
2085 if self.high_mark is not None:
2086 self.high_mark = min(self.high_mark, self.low_mark + high)
2087 else:
2088 self.high_mark = self.low_mark + high
2089 if low is not None:
2090 if self.high_mark is not None:
2091 self.low_mark = min(self.high_mark, self.low_mark + low)
2092 else:
2093 self.low_mark = self.low_mark + low
2094
2095 if self.low_mark == self.high_mark:
2096 self.set_empty()
2097
2098 def clear_limits(self) -> None:
2099 """Clear any existing limits."""
2100 self.low_mark, self.high_mark = 0, None
2101
2102 @property
2103 def is_sliced(self) -> bool:
2104 return self.low_mark != 0 or self.high_mark is not None
2105
2106 def has_limit_one(self) -> bool:
2107 return self.high_mark is not None and (self.high_mark - self.low_mark) == 1
2108
2109 def can_filter(self) -> bool:
2110 """
2111 Return True if adding filters to this instance is still possible.
2112
2113 Typically, this means no limits or offsets have been put on the results.
2114 """
2115 return not self.is_sliced
2116
2117 def clear_select_clause(self) -> None:
2118 """Remove all fields from SELECT clause."""
2119 self.select = ()
2120 self.default_cols = False
2121 self.select_related = False
2122 self.set_extra_mask(())
2123 self.set_annotation_mask(())
2124
2125 def clear_select_fields(self) -> None:
2126 """
2127 Clear the list of fields to select (but not extra_select columns).
2128 Some queryset types completely replace any existing list of select
2129 columns.
2130 """
2131 self.select = ()
2132 self.values_select = ()
2133
2134 def add_select_col(self, col: Col, name: str) -> None:
2135 self.select += (col,)
2136 self.values_select += (name,)
2137
2138 def set_select(self, cols: list[Col] | tuple[Col, ...]) -> None:
2139 self.default_cols = False
2140 self.select = tuple(cols)
2141
2142 def add_distinct_fields(self, *field_names: str) -> None:
2143 """
2144 Add and resolve the given fields to the query's "distinct on" clause.
2145 """
2146 self.distinct_fields = field_names
2147 self.distinct = True
2148
2149 def add_fields(
2150 self, field_names: list[str] | TypingIterator[str], allow_m2m: bool = True
2151 ) -> None:
2152 """
2153 Add the given (model) fields to the select set. Add the field names in
2154 the order specified.
2155 """
2156 alias = self.get_initial_alias()
2157 assert alias is not None
2158 meta = self.get_model_meta()
2159
2160 try:
2161 cols = []
2162 for name in field_names:
2163 # Join promotion note - we must not remove any rows here, so
2164 # if there is no existing joins, use outer join.
2165 join_info = self.setup_joins(
2166 name.split(LOOKUP_SEP), meta, alias, allow_many=allow_m2m
2167 )
2168 targets, final_alias, joins = self.trim_joins(
2169 join_info.targets,
2170 join_info.joins,
2171 join_info.path,
2172 )
2173 for target in targets:
2174 cols.append(join_info.transform_function(target, final_alias))
2175 if cols:
2176 self.set_select(cols)
2177 except MultiJoin:
2178 raise FieldError(f"Invalid field name: '{name}'")
2179 except FieldError:
2180 if LOOKUP_SEP in name:
2181 # For lookups spanning over relationships, show the error
2182 # from the model on which the lookup failed.
2183 raise
2184 elif name in self.annotations:
2185 raise FieldError(
2186 f"Cannot select the '{name}' alias. Use annotate() to promote it."
2187 )
2188 else:
2189 names = sorted(
2190 [
2191 *get_field_names_from_opts(meta),
2192 *self.extra,
2193 *self.annotation_select,
2194 *self._filtered_relations,
2195 ]
2196 )
2197 raise FieldError(
2198 "Cannot resolve keyword {!r} into field. Choices are: {}".format(
2199 name, ", ".join(names)
2200 )
2201 )
2202
2203 def add_ordering(self, *ordering: str | BaseExpression) -> None:
2204 """
2205 Add items from the 'ordering' sequence to the query's "order by"
2206 clause. These items are either field names (not column names) --
2207 possibly with a direction prefix ('-' or '?') -- or OrderBy
2208 expressions.
2209
2210 If 'ordering' is empty, clear all ordering from the query.
2211 """
2212 errors = []
2213 for item in ordering:
2214 if isinstance(item, str):
2215 if item == "?":
2216 continue
2217 item = item.removeprefix("-")
2218 if item in self.annotations:
2219 continue
2220 if self.extra and item in self.extra:
2221 continue
2222 # names_to_path() validates the lookup. A descriptive
2223 # FieldError will be raise if it's not.
2224 self.names_to_path(item.split(LOOKUP_SEP), self.model._model_meta)
2225 elif not hasattr(item, "resolve_expression"):
2226 errors.append(item)
2227 if getattr(item, "contains_aggregate", False):
2228 raise FieldError(
2229 "Using an aggregate in order_by() without also including "
2230 f"it in annotate() is not allowed: {item}"
2231 )
2232 if errors:
2233 raise FieldError(f"Invalid order_by arguments: {errors}")
2234 if ordering:
2235 self.order_by += ordering
2236 else:
2237 self.default_ordering = False
2238
2239 def clear_ordering(self, force: bool = False, clear_default: bool = True) -> None:
2240 """
2241 Remove any ordering settings if the current query allows it without
2242 side effects, set 'force' to True to clear the ordering regardless.
2243 If 'clear_default' is True, there will be no ordering in the resulting
2244 query (not even the model's default).
2245 """
2246 if not force and (
2247 self.is_sliced or self.distinct_fields or self.select_for_update
2248 ):
2249 return
2250 self.order_by = ()
2251 self.extra_order_by = ()
2252 if clear_default:
2253 self.default_ordering = False
2254
2255 def set_group_by(self, allow_aliases: bool = True) -> None:
2256 """
2257 Expand the GROUP BY clause required by the query.
2258
2259 This will usually be the set of all non-aggregate fields in the
2260 return data. If the database backend supports grouping by the
2261 primary key, and the query would be equivalent, the optimization
2262 will be made automatically.
2263 """
2264 if allow_aliases and self.values_select:
2265 # If grouping by aliases is allowed assign selected value aliases
2266 # by moving them to annotations.
2267 group_by_annotations = {}
2268 values_select = {}
2269 for alias, expr in zip(self.values_select, self.select):
2270 if isinstance(expr, Col):
2271 values_select[alias] = expr
2272 else:
2273 group_by_annotations[alias] = expr
2274 self.annotations = {**group_by_annotations, **self.annotations}
2275 self.append_annotation_mask(group_by_annotations)
2276 self.select = tuple(values_select.values())
2277 self.values_select = tuple(values_select)
2278 group_by = list(self.select)
2279 for alias, annotation in self.annotation_select.items():
2280 if not (group_by_cols := annotation.get_group_by_cols()):
2281 continue
2282 if allow_aliases and not annotation.contains_aggregate:
2283 group_by.append(Ref(alias, annotation))
2284 else:
2285 group_by.extend(group_by_cols)
2286 self.group_by = tuple(group_by)
2287
2288 def add_select_related(self, fields: list[str]) -> None:
2289 """
2290 Set up the select_related data structure so that we only select
2291 certain related models (as opposed to all models, when
2292 self.select_related=True).
2293 """
2294 if isinstance(self.select_related, bool):
2295 field_dict: dict[str, Any] = {}
2296 else:
2297 field_dict = self.select_related
2298 for field in fields:
2299 d = field_dict
2300 for part in field.split(LOOKUP_SEP):
2301 d = d.setdefault(part, {})
2302 self.select_related = field_dict
2303
2304 def add_extra(
2305 self,
2306 select: dict[str, str],
2307 select_params: list[Any] | None,
2308 where: list[str],
2309 params: list[Any],
2310 tables: list[str],
2311 order_by: tuple[str, ...],
2312 ) -> None:
2313 """
2314 Add data to the various extra_* attributes for user-created additions
2315 to the query.
2316 """
2317 if select:
2318 # We need to pair any placeholder markers in the 'select'
2319 # dictionary with their parameters in 'select_params' so that
2320 # subsequent updates to the select dictionary also adjust the
2321 # parameters appropriately.
2322 select_pairs = {}
2323 if select_params:
2324 param_iter = iter(select_params)
2325 else:
2326 param_iter = iter([])
2327 for name, entry in select.items():
2328 self.check_alias(name)
2329 entry = str(entry)
2330 entry_params = []
2331 pos = entry.find("%s")
2332 while pos != -1:
2333 if pos == 0 or entry[pos - 1] != "%":
2334 entry_params.append(next(param_iter))
2335 pos = entry.find("%s", pos + 2)
2336 select_pairs[name] = (entry, entry_params)
2337 self.extra.update(select_pairs)
2338 if where or params:
2339 self.where.add(ExtraWhere(where, params), AND)
2340 if tables:
2341 self.extra_tables += tuple(tables)
2342 if order_by:
2343 self.extra_order_by = order_by
2344
2345 def clear_deferred_loading(self) -> None:
2346 """Remove any fields from the deferred loading set."""
2347 self.deferred_loading = (frozenset(), True)
2348
2349 def add_deferred_loading(self, field_names: frozenset[str]) -> None:
2350 """
2351 Add the given list of model field names to the set of fields to
2352 exclude from loading from the database when automatic column selection
2353 is done. Add the new field names to any existing field names that
2354 are deferred (or removed from any existing field names that are marked
2355 as the only ones for immediate loading).
2356 """
2357 # Fields on related models are stored in the literal double-underscore
2358 # format, so that we can use a set datastructure. We do the foo__bar
2359 # splitting and handling when computing the SQL column names (as part of
2360 # get_columns()).
2361 existing, defer = self.deferred_loading
2362 existing_set = set(existing)
2363 if defer:
2364 # Add to existing deferred names.
2365 self.deferred_loading = frozenset(existing_set.union(field_names)), True
2366 else:
2367 # Remove names from the set of any existing "immediate load" names.
2368 if new_existing := existing_set.difference(field_names):
2369 self.deferred_loading = frozenset(new_existing), False
2370 else:
2371 self.clear_deferred_loading()
2372 if new_only := set(field_names).difference(existing_set):
2373 self.deferred_loading = frozenset(new_only), True
2374
2375 def add_immediate_loading(self, field_names: list[str] | set[str]) -> None:
2376 """
2377 Add the given list of model field names to the set of fields to
2378 retrieve when the SQL is executed ("immediate loading" fields). The
2379 field names replace any existing immediate loading field names. If
2380 there are field names already specified for deferred loading, remove
2381 those names from the new field_names before storing the new names
2382 for immediate loading. (That is, immediate loading overrides any
2383 existing immediate values, but respects existing deferrals.)
2384 """
2385 existing, defer = self.deferred_loading
2386 field_names_set = set(field_names)
2387
2388 if defer:
2389 # Remove any existing deferred names from the current set before
2390 # setting the new names.
2391 self.deferred_loading = (
2392 frozenset(field_names_set.difference(existing)),
2393 False,
2394 )
2395 else:
2396 # Replace any existing "immediate load" field names.
2397 self.deferred_loading = frozenset(field_names_set), False
2398
2399 def set_annotation_mask(
2400 self,
2401 names: set[str]
2402 | frozenset[str]
2403 | list[str]
2404 | tuple[str, ...]
2405 | dict[str, Any]
2406 | None,
2407 ) -> None: # type: ignore[misc]
2408 """Set the mask of annotations that will be returned by the SELECT."""
2409 if names is None:
2410 self.annotation_select_mask = None
2411 else:
2412 self.annotation_select_mask = set(names)
2413 self._annotation_select_cache = None
2414
2415 def append_annotation_mask(self, names: list[str] | dict[str, Any]) -> None:
2416 if self.annotation_select_mask is not None:
2417 self.set_annotation_mask(self.annotation_select_mask.union(names))
2418
2419 def set_extra_mask(
2420 self, names: set[str] | list[str] | tuple[str, ...] | None
2421 ) -> None:
2422 """
2423 Set the mask of extra select items that will be returned by SELECT.
2424 Don't remove them from the Query since they might be used later.
2425 """
2426 if names is None:
2427 self.extra_select_mask = None
2428 else:
2429 self.extra_select_mask = set(names)
2430 self._extra_select_cache = None
2431
2432 def set_values(self, fields: list[str]) -> None:
2433 self.select_related = False
2434 self.clear_deferred_loading()
2435 self.clear_select_fields()
2436 self.has_select_fields = True
2437
2438 if fields:
2439 field_names = []
2440 extra_names = []
2441 annotation_names = []
2442 if not self.extra and not self.annotations:
2443 # Shortcut - if there are no extra or annotations, then
2444 # the values() clause must be just field names.
2445 field_names = list(fields)
2446 else:
2447 self.default_cols = False
2448 for f in fields:
2449 if f in self.extra_select:
2450 extra_names.append(f)
2451 elif f in self.annotation_select:
2452 annotation_names.append(f)
2453 else:
2454 field_names.append(f)
2455 self.set_extra_mask(extra_names)
2456 self.set_annotation_mask(annotation_names)
2457 selected = frozenset(field_names + extra_names + annotation_names)
2458 else:
2459 field_names = [f.attname for f in self.model._model_meta.concrete_fields]
2460 selected = frozenset(field_names)
2461 # Selected annotations must be known before setting the GROUP BY
2462 # clause.
2463 if self.group_by is True:
2464 self.add_fields(
2465 (f.attname for f in self.model._model_meta.concrete_fields), False
2466 )
2467 # Disable GROUP BY aliases to avoid orphaning references to the
2468 # SELECT clause which is about to be cleared.
2469 self.set_group_by(allow_aliases=False)
2470 self.clear_select_fields()
2471 elif self.group_by:
2472 # Resolve GROUP BY annotation references if they are not part of
2473 # the selected fields anymore.
2474 group_by = []
2475 for expr in self.group_by:
2476 if isinstance(expr, Ref) and expr.refs not in selected:
2477 expr = self.annotations[expr.refs]
2478 group_by.append(expr)
2479 self.group_by = tuple(group_by)
2480
2481 self.values_select = tuple(field_names)
2482 self.add_fields(field_names, True)
2483
2484 @property
2485 def annotation_select(self) -> dict[str, BaseExpression]:
2486 """
2487 Return the dictionary of aggregate columns that are not masked and
2488 should be used in the SELECT clause. Cache this result for performance.
2489 """
2490 if self._annotation_select_cache is not None:
2491 return self._annotation_select_cache
2492 elif not self.annotations:
2493 return {}
2494 elif self.annotation_select_mask is not None:
2495 self._annotation_select_cache = {
2496 k: v
2497 for k, v in self.annotations.items()
2498 if k in self.annotation_select_mask
2499 }
2500 return self._annotation_select_cache
2501 else:
2502 return self.annotations
2503
2504 @property
2505 def extra_select(self) -> dict[str, tuple[str, list[Any]]]:
2506 if self._extra_select_cache is not None:
2507 return self._extra_select_cache
2508 if not self.extra:
2509 return {}
2510 elif self.extra_select_mask is not None:
2511 self._extra_select_cache = {
2512 k: v for k, v in self.extra.items() if k in self.extra_select_mask
2513 }
2514 return self._extra_select_cache
2515 else:
2516 return self.extra
2517
2518 def trim_start(
2519 self, names_with_path: list[tuple[str, list[Any]]]
2520 ) -> tuple[str, bool]:
2521 """
2522 Trim joins from the start of the join path. The candidates for trim
2523 are the PathInfos in names_with_path structure that are m2m joins.
2524
2525 Also set the select column so the start matches the join.
2526
2527 This method is meant to be used for generating the subquery joins &
2528 cols in split_exclude().
2529
2530 Return a lookup usable for doing outerq.filter(lookup=self) and a
2531 boolean indicating if the joins in the prefix contain a LEFT OUTER join.
2532 _"""
2533 all_paths = []
2534 for _, paths in names_with_path:
2535 all_paths.extend(paths)
2536 contains_louter = False
2537 # Trim and operate only on tables that were generated for
2538 # the lookup part of the query. That is, avoid trimming
2539 # joins generated for F() expressions.
2540 lookup_tables = [
2541 t for t in self.alias_map if t in self._lookup_joins or t == self.base_table
2542 ]
2543 for trimmed_paths, path in enumerate(all_paths):
2544 if path.m2m:
2545 break
2546 if self.alias_map[lookup_tables[trimmed_paths + 1]].join_type == LOUTER:
2547 contains_louter = True
2548 alias = lookup_tables[trimmed_paths]
2549 self.unref_alias(alias)
2550 # The path.join_field is a Rel, lets get the other side's field
2551 join_field = path.join_field.field
2552 # Build the filter prefix.
2553 paths_in_prefix = trimmed_paths
2554 trimmed_prefix = []
2555 for name, path in names_with_path:
2556 if paths_in_prefix - len(path) < 0:
2557 break
2558 trimmed_prefix.append(name)
2559 paths_in_prefix -= len(path)
2560 trimmed_prefix.append(join_field.foreign_related_fields[0].name)
2561 trimmed_prefix = LOOKUP_SEP.join(trimmed_prefix)
2562 # Lets still see if we can trim the first join from the inner query
2563 # (that is, self). We can't do this for:
2564 # - LEFT JOINs because we would miss those rows that have nothing on
2565 # the outer side,
2566 # - INNER JOINs from filtered relations because we would miss their
2567 # filters.
2568 first_join = self.alias_map[lookup_tables[trimmed_paths + 1]]
2569 if first_join.join_type != LOUTER and not first_join.filtered_relation:
2570 select_fields = [r[0] for r in join_field.related_fields]
2571 select_alias = lookup_tables[trimmed_paths + 1]
2572 self.unref_alias(lookup_tables[trimmed_paths])
2573 extra_restriction = join_field.get_extra_restriction(
2574 None, lookup_tables[trimmed_paths + 1]
2575 )
2576 if extra_restriction:
2577 self.where.add(extra_restriction, AND)
2578 else:
2579 # TODO: It might be possible to trim more joins from the start of the
2580 # inner query if it happens to have a longer join chain containing the
2581 # values in select_fields. Lets punt this one for now.
2582 select_fields = [r[1] for r in join_field.related_fields]
2583 select_alias = lookup_tables[trimmed_paths]
2584 # The found starting point is likely a join_class instead of a
2585 # base_table_class reference. But the first entry in the query's FROM
2586 # clause must not be a JOIN.
2587 for table in self.alias_map:
2588 if self.alias_refcount[table] > 0:
2589 self.alias_map[table] = self.base_table_class(
2590 self.alias_map[table].table_name,
2591 table,
2592 )
2593 break
2594 self.set_select([f.get_col(select_alias) for f in select_fields])
2595 return trimmed_prefix, contains_louter
2596
2597 def is_nullable(self, field: Field) -> bool:
2598 """Check if the given field should be treated as nullable."""
2599 # QuerySet does not have knowledge of which connection is going to be
2600 # used. For the single-database setup we always reference the default
2601 # connection here.
2602 return field.allow_null
2603
2604
2605def get_order_dir(field: str, default: str = "ASC") -> tuple[str, str]:
2606 """
2607 Return the field name and direction for an order specification. For
2608 example, '-foo' is returned as ('foo', 'DESC').
2609
2610 The 'default' param is used to indicate which way no prefix (or a '+'
2611 prefix) should sort. The '-' prefix always sorts the opposite way.
2612 """
2613 dirn = ORDER_DIR[default]
2614 if field[0] == "-":
2615 return field[1:], dirn[1]
2616 return field, dirn[0]
2617
2618
2619class JoinPromoter:
2620 """
2621 A class to abstract away join promotion problems for complex filter
2622 conditions.
2623 """
2624
2625 def __init__(self, connector: str, num_children: int, negated: bool):
2626 self.connector = connector
2627 self.negated = negated
2628 if self.negated:
2629 if connector == AND:
2630 self.effective_connector = OR
2631 else:
2632 self.effective_connector = AND
2633 else:
2634 self.effective_connector = self.connector
2635 self.num_children = num_children
2636 # Maps of table alias to how many times it is seen as required for
2637 # inner and/or outer joins.
2638 self.votes = Counter()
2639
2640 def __repr__(self) -> str:
2641 return (
2642 f"{self.__class__.__qualname__}(connector={self.connector!r}, "
2643 f"num_children={self.num_children!r}, negated={self.negated!r})"
2644 )
2645
2646 def add_votes(self, votes: Any) -> None:
2647 """
2648 Add single vote per item to self.votes. Parameter can be any
2649 iterable.
2650 """
2651 self.votes.update(votes)
2652
2653 def update_join_types(self, query: Query) -> set[str]:
2654 """
2655 Change join types so that the generated query is as efficient as
2656 possible, but still correct. So, change as many joins as possible
2657 to INNER, but don't make OUTER joins INNER if that could remove
2658 results from the query.
2659 """
2660 to_promote = set()
2661 to_demote = set()
2662 # The effective_connector is used so that NOT (a AND b) is treated
2663 # similarly to (a OR b) for join promotion.
2664 for table, votes in self.votes.items():
2665 # We must use outer joins in OR case when the join isn't contained
2666 # in all of the joins. Otherwise the INNER JOIN itself could remove
2667 # valid results. Consider the case where a model with rel_a and
2668 # rel_b relations is queried with rel_a__col=1 | rel_b__col=2. Now,
2669 # if rel_a join doesn't produce any results is null (for example
2670 # reverse foreign key or null value in direct foreign key), and
2671 # there is a matching row in rel_b with col=2, then an INNER join
2672 # to rel_a would remove a valid match from the query. So, we need
2673 # to promote any existing INNER to LOUTER (it is possible this
2674 # promotion in turn will be demoted later on).
2675 if self.effective_connector == OR and votes < self.num_children:
2676 to_promote.add(table)
2677 # If connector is AND and there is a filter that can match only
2678 # when there is a joinable row, then use INNER. For example, in
2679 # rel_a__col=1 & rel_b__col=2, if either of the rels produce NULL
2680 # as join output, then the col=1 or col=2 can't match (as
2681 # NULL=anything is always false).
2682 # For the OR case, if all children voted for a join to be inner,
2683 # then we can use INNER for the join. For example:
2684 # (rel_a__col__icontains=Alex | rel_a__col__icontains=Russell)
2685 # then if rel_a doesn't produce any rows, the whole condition
2686 # can't match. Hence we can safely use INNER join.
2687 if self.effective_connector == AND or (
2688 self.effective_connector == OR and votes == self.num_children
2689 ):
2690 to_demote.add(table)
2691 # Finally, what happens in cases where we have:
2692 # (rel_a__col=1|rel_b__col=2) & rel_a__col__gte=0
2693 # Now, we first generate the OR clause, and promote joins for it
2694 # in the first if branch above. Both rel_a and rel_b are promoted
2695 # to LOUTER joins. After that we do the AND case. The OR case
2696 # voted no inner joins but the rel_a__col__gte=0 votes inner join
2697 # for rel_a. We demote it back to INNER join (in AND case a single
2698 # vote is enough). The demotion is OK, if rel_a doesn't produce
2699 # rows, then the rel_a__col__gte=0 clause can't be true, and thus
2700 # the whole clause must be false. So, it is safe to use INNER
2701 # join.
2702 # Note that in this example we could just as well have the __gte
2703 # clause and the OR clause swapped. Or we could replace the __gte
2704 # clause with an OR clause containing rel_a__col=1|rel_a__col=2,
2705 # and again we could safely demote to INNER.
2706 query.promote_joins(to_promote)
2707 query.demote_joins(to_demote)
2708 return to_demote