MCPcopy
hub / github.com/django/django / JoinPromoter

Class JoinPromoter

django/db/models/sql/query.py:2808–2897  ·  view source on GitHub ↗

A class to abstract away join promotion problems for complex filter conditions.

Source from the content-addressed store, hash-verified

2806
2807
2808class JoinPromoter:
2809 """
2810 A class to abstract away join promotion problems for complex filter
2811 conditions.
2812 """
2813
2814 def __init__(self, connector, num_children, negated):
2815 self.connector = connector
2816 self.negated = negated
2817 if self.negated:
2818 if connector == AND:
2819 self.effective_connector = OR
2820 else:
2821 self.effective_connector = AND
2822 else:
2823 self.effective_connector = self.connector
2824 self.num_children = num_children
2825 # Maps of table alias to how many times it is seen as required for
2826 # inner and/or outer joins.
2827 self.votes = Counter()
2828
2829 def __repr__(self):
2830 return (
2831 f"{self.__class__.__qualname__}(connector={self.connector!r}, "
2832 f"num_children={self.num_children!r}, negated={self.negated!r})"
2833 )
2834
2835 def add_votes(self, votes):
2836 """
2837 Add single vote per item to self.votes. Parameter can be any
2838 iterable.
2839 """
2840 self.votes.update(votes)
2841
2842 def update_join_types(self, query):
2843 """
2844 Change join types so that the generated query is as efficient as
2845 possible, but still correct. So, change as many joins as possible
2846 to INNER, but don't make OUTER joins INNER if that could remove
2847 results from the query.
2848 """
2849 to_promote = set()
2850 to_demote = set()
2851 # The effective_connector is used so that NOT (a AND b) is treated
2852 # similarly to (a OR b) for join promotion.
2853 for table, votes in self.votes.items():
2854 # We must use outer joins in OR case when the join isn't contained
2855 # in all of the joins. Otherwise the INNER JOIN itself could remove
2856 # valid results. Consider the case where a model with rel_a and
2857 # rel_b relations is queried with rel_a__col=1 | rel_b__col=2. Now,
2858 # if rel_a join doesn't produce any results is null (for example
2859 # reverse foreign key or null value in direct foreign key), and
2860 # there is a matching row in rel_b with col=2, then an INNER join
2861 # to rel_a would remove a valid match from the query. So, we need
2862 # to promote any existing INNER to LOUTER (it is possible this
2863 # promotion in turn will be demoted later on).
2864 if self.effective_connector == OR and votes < self.num_children:
2865 to_promote.add(table)

Callers 3

test_reprMethod · 0.90
combineMethod · 0.85
_add_qMethod · 0.85

Calls

no outgoing calls

Tested by 1

test_reprMethod · 0.72