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