@@ -666,56 +666,56 @@ def _get_pushable_conditions(self):
666666 Return a dict mapping each alias to a WhereNode holding its pushable
667667 condition.
668668 """
669+ return self ._collect_pushable (self .get_where ())
669670
670- def collect_pushable (expr , negated = False ):
671- if expr is None or isinstance (expr , NothingNode ):
671+ @classmethod
672+ def _collect_pushable (cls , expr , negated = False ):
673+ if expr is None or isinstance (expr , NothingNode ):
674+ return {}
675+ if isinstance (expr , WhereNode ):
676+ # Apply De Morgan: track negation so connectors are flipped
677+ # when needed.
678+ negated ^= expr .negated
679+ pushable_expressions = [
680+ cls ._collect_pushable (sub_expr , negated = negated )
681+ for sub_expr in expr .children
682+ if sub_expr is not None
683+ ]
684+ operator = expr .connector
685+ if operator == XOR :
672686 return {}
673- if isinstance (expr , WhereNode ):
674- # Apply De Morgan: track negation so connectors are flipped
675- # when needed.
676- negated ^= expr .negated
677- pushable_expressions = [
678- collect_pushable (sub_expr , negated = negated )
679- for sub_expr in expr .children
680- if sub_expr is not None
681- ]
682- operator = expr .connector
683- if operator == XOR :
684- return {}
685- if negated :
686- operator = OR if operator == AND else AND
687- alias_children = defaultdict (list )
688- for pe in pushable_expressions :
689- for alias , expressions in pe .items ():
690- alias_children [alias ].append (expressions )
691- # Build per-alias pushable condition nodes.
692- if operator == AND :
693- return {
694- alias : WhereNode (children = children , negated = False , connector = operator )
695- for alias , children in alias_children .items ()
696- }
697- # Only aliases shared across all branches are pushable for OR.
698- shared_alias = (
699- set .intersection (* (set (pe ) for pe in pushable_expressions ))
700- if pushable_expressions
701- else set ()
702- )
687+ if negated :
688+ operator = OR if operator == AND else AND
689+ alias_children = defaultdict (list )
690+ for pe in pushable_expressions :
691+ for alias , expressions in pe .items ():
692+ alias_children [alias ].append (expressions )
693+ # Build per-alias pushable condition nodes.
694+ if operator == AND :
703695 return {
704696 alias : WhereNode (children = children , negated = False , connector = operator )
705697 for alias , children in alias_children .items ()
706- if alias in shared_alias
707698 }
708- # A leaf is pushable only when comparing a field to a constant or
709- # simple value.
710- if isinstance (expr .lhs , Col ) and (
711- is_constant_value (expr .rhs ) or getattr (expr .rhs , "is_simple_column" , False )
712- ):
713- alias = expr .lhs .alias
714- expr = WhereNode (children = [expr ], negated = negated )
715- return {alias : expr }
716- return {}
717-
718- return collect_pushable (self .get_where ())
699+ # Only aliases shared across all branches are pushable for OR.
700+ shared_alias = (
701+ set .intersection (* (set (pe ) for pe in pushable_expressions ))
702+ if pushable_expressions
703+ else set ()
704+ )
705+ return {
706+ alias : WhereNode (children = children , negated = False , connector = operator )
707+ for alias , children in alias_children .items ()
708+ if alias in shared_alias
709+ }
710+ # A leaf is pushable only when comparing a field to a constant or
711+ # simple value.
712+ if isinstance (expr .lhs , Col ) and (
713+ is_constant_value (expr .rhs ) or getattr (expr .rhs , "is_simple_column" , False )
714+ ):
715+ alias = expr .lhs .alias
716+ expr = WhereNode (children = [expr ], negated = negated )
717+ return {alias : expr }
718+ return {}
719719
720720 def get_lookup_pipeline (self ):
721721 result = []
0 commit comments