warn the user as soon as possible if they are trying to apply a bilateral transformation on a nested queryset that won t work wrapped in parentheses to respect operator precedence do not call getdbpreplookup here as the value will be transformed before being used for lookup ensure expression is wrapped in parentheses to respect operator precedence but avoid double wrapping as it can be misinterpreted on some backends e g subqueries on sqlite oracle doesn t allow exists and filters to be compared to another expression unless they re wrapped in a case when wrap filters with a case when expression if a database backend e g oracle doesn t support boolean expression in select or group by list registerlookupmixin is first so that getlookup and gettransform first examine self and then check outputfield some lookups require field getdbprepvalue to be called on their inputs for relational fields use the targetfield attribute of the outputfield some lookups require field getdbprepvalue to be called on each value in an iterable an expression will be handled by the database but can coexist alongside real values rhs should be an iterable of values use batchprocessrhs to preparetransform those values the params list may contain expressions which compile to a sqlparam pair zip them to get sql and param pairs that refer to the same argument and attempt to replace them with the result of compiling the param step lookup defined by operators on postgresql postgresoperator none def aspostgresqlself compiler connection lhs lhsparams self processlhscompiler connection rhs rhsparams self processrhscompiler connection params tuplelhsparams tuplerhsparams return s s s lhs self postgresoperator rhs params field registerlookup class exactfieldgetdbprepvaluemixin builtinlookup lookupname exact def getpreplookupself from django db models sql query import query avoid circular import if isinstanceself rhs query if self rhs haslimitone if not self rhs hasselectfields self rhs clearselectclause self rhs addfieldspk else raise valueerror the queryset value for an exact lookup must be limited to one result using slicing return super getpreplookup def assqlself compiler connection avoid comparison against direct rhs if lhs is a boolean value that turns boolfieldexacttrue into where booleanfield instead of where booleanfield true when allowed if isinstanceself rhs bool and getattrself lhs conditional false and connection ops conditionalexpressionsupportedinwhereclause self lhs lhssql params self processlhscompiler connection template s if self rhs else not s return template lhssql params return super assqlcompiler connection field registerlookup class iexactbuiltinlookup lookupname iexact preparerhs false def processrhsself qn connection rhs params super processrhsqn connection if params params0 connection ops prepforiexactqueryparams0 return rhs params field registerlookup class greaterthanfieldgetdbprepvaluemixin builtinlookup lookupname gt field registerlookup class greaterthanorequalfieldgetdbprepvaluemixin builtinlookup lookupname gte field registerlookup class lessthanfieldgetdbprepvaluemixin builtinlookup lookupname lt field registerlookup class lessthanorequalfieldgetdbprepvaluemixin builtinlookup lookupname lte class integerfieldoverflow underflowexception emptyresultset overflowexception emptyresultset def processrhsself compiler connection rhs self rhs if isinstancerhs int fieldinternaltype self lhs outputfield getinternaltype minvalue maxvalue connection ops integerfieldrange fieldinternaltype if minvalue is not none and rhs minvalue raise self underflowexception if maxvalue is not none and rhs maxvalue raise self overflowexception return super processrhscompiler connection class integerfieldfloatrounding def getpreplookupself if isinstanceself rhs float self rhs math ceilself rhs return super getpreplookup integerfield registerlookup class integerfieldexactintegerfieldoverflow exact pass integerfield registerlookup class integergreaterthanintegerfieldoverflow greaterthan underflowexception fullresultset integerfield registerlookup class integergreaterthanorequal integerfieldoverflow integerfieldfloatrounding greaterthanorequal underflowexception fullresultset integerfield registerlookup class integerlessthanintegerfieldoverflow integerfieldfloatrounding lessthan overflowexception fullresultset integerfield registerlookup class integerlessthanorequalintegerfieldoverflow lessthanorequal overflowexception fullresultset field registerlookup class infieldgetdbprepvalueiterablemixin builtinlookup lookupname in def getrefsself refs super getrefs if self rhsisdirectvalue for rhs in self rhs if getrhsrefs getattrrhs getrefs none refs getrhsrefs return refs def getpreplookupself from django db models sql query import query avoid circular import if isinstanceself rhs query self rhs clearorderingcleardefaulttrue if not self rhs hasselectfields self rhs clearselectclause self rhs addfieldspk return super getpreplookup def processrhsself compiler connection dbrhs getattrself rhs db none if dbrhs is not none and dbrhs connection alias raise valueerror subqueries aren t allowed across different databases force the inner query to be evaluated using listinnerquery if self rhsisdirectvalue remove none from the list as null is never equal to anything try rhs orderedsetself rhs rhs discardnone except typeerror unhashable items in self rhs rhs r for r in self rhs if r is not none if not rhs raise emptyresultset rhs should be an iterable use batchprocessrhs to preparetransform those values sqls sqlsparams self batchprocessrhscompiler connection rhs placeholder joinsqls return placeholder sqlsparams return super processrhscompiler connection def getrhsopself connection rhs return in s rhs def assqlself compiler connection maxinlistsize connection ops maxinlistsize if self rhsisdirectvalue and maxinlistsize and lenself rhs maxinlistsize return self splitparameterlistassqlcompiler connection return super assqlcompiler connection def splitparameterlistassqlself compiler connection this is a special case for databases which limit the number of elements which can appear in an in clause maxinlistsize connection ops maxinlistsize lhs lhsparams self processlhscompiler connection rhs rhsparams self batchprocessrhscompiler connection inclauseelements params for offset in range0 lenrhsparams maxinlistsize if offset 0 inclauseelements append or inclauseelements appends in lhs params extendlhsparams sqls rhsoffset offset maxinlistsize sqlsparams rhsparamsoffset offset maxinlistsize paramgroup joinsqls inclauseelements appendparamgroup inclauseelements append params extendsqlsparams inclauseelements append return joininclauseelements params class patternlookupbuiltinlookup parampattern s preparerhs false def getrhsopself connection rhs assume we are in startswith we need to produce sql like col like s thevalue for python values we can and should do that directly in python but if the value is for example reference to other column then we need to add the pattern match to the lookup by something like col like othercol so for python values we don t need any special pattern but for sql reference values or sql transformations we need the correct pattern added if hasattrself rhs assql or self bilateraltransforms pattern connection patternopsself lookupname format connection patternesc return pattern formatrhs else return super getrhsopconnection rhs def processrhsself qn connection rhs params super processrhsqn connection if self rhsisdirectvalue and params and not self bilateraltransforms params0 self parampattern connection ops prepforlikequery params0 return rhs params field registerlookup class containspatternlookup lookupname contains field registerlookup class icontainscontains lookupname icontains field registerlookup class startswithpatternlookup lookupname startswith parampattern s field registerlookup class istartswithstartswith lookupname istartswith field registerlookup class endswithpatternlookup lookupname endswith parampattern s field registerlookup class iendswithendswith lookupname iendswith field registerlookup class rangefieldgetdbprepvalueiterablemixin builtinlookup lookupname range def getrhsopself connection rhs return between s and s rhs0 rhs1 field registerlookup class isnullbuiltinlookup lookupname isnull preparerhs false def assqlself compiler connection if not isinstanceself rhs bool raise valueerror the queryset value for an isnull lookup must be true or false if isinstanceself lhs value if self lhs value is none or self lhs value and connection features interpretsemptystringsasnulls resultexception fullresultset if self rhs else emptyresultset else resultexception emptyresultset if self rhs else fullresultset raise resultexception sql params self processlhscompiler connection if self rhs return s is null sql params else return s is not null sql params field registerlookup class regexbuiltinlookup lookupname regex preparerhs false def assqlself compiler connection if self lookupname in connection operators return super assqlcompiler connection else lhs lhsparams self processlhscompiler connection rhs rhsparams self processrhscompiler connection sqltemplate connection ops regexlookupself lookupname return sqltemplate lhs rhs lhsparams rhsparams field registerlookup class iregexregex lookupname iregex class yearlookuplookup def yearlookupboundsself connection year from django db models functions import extractisoyear isoyear isinstanceself lhs extractisoyear outputfield self lhs lhs outputfield if isinstanceoutputfield datetimefield bounds connection ops yearlookupboundsfordatetimefield year isoyearisoyear else bounds connection ops yearlookupboundsfordatefield year isoyearisoyear return bounds def assqlself compiler connection avoid the extract operation if the rhs is a direct value to allow indexes to be used if self rhsisdirectvalue skip the extract part by directly using the originating field that is self lhs lhs lhssql params self processlhscompiler connection self lhs lhs rhssql self processrhscompiler connection rhssql self getdirectrhssqlconnection rhssql start finish self yearlookupboundsconnection self rhs params extendself getboundparamsstart finish return s s lhssql rhssql params return super assqlcompiler connection def getdirectrhssqlself connection rhs return connection operatorsself lookupname rhs def getboundparamsself start finish raise notimplementederror subclasses of yearlookup must provide a getboundparams method class yearexactyearlookup exact def getdirectrhssqlself connection rhs return between s and s def getboundparamsself start finish return start finish class yeargtyearlookup greaterthan def getboundparamsself start finish return finish class yeargteyearlookup greaterthanorequal def getboundparamsself start finish return start class yearltyearlookup lessthan def getboundparamsself start finish return start class yearlteyearlookup lessthanorequal def getboundparamsself start finish return finish class uuidtextmixin def processrhsself qn connection if not connection features hasnativeuuidfield from django db models functions import replace if self rhsisdirectvalue self rhs valueself rhs self rhs replace self rhs value value outputfieldcharfield rhs params super processrhsqn connection return rhs params uuidfield registerlookup class uuidiexactuuidtextmixin iexact pass uuidfield registerlookup class uuidcontainsuuidtextmixin contains pass uuidfield registerlookup class uuidicontainsuuidtextmixin icontains pass uuidfield registerlookup class uuidstartswithuuidtextmixin startswith pass uuidfield registerlookup class uuidistartswithuuidtextmixin istartswith pass uuidfield registerlookup class uuidendswithuuidtextmixin endswith pass uuidfield registerlookup class uuidiendswithuuidtextmixin iendswith pass warn the user as soon as possible if they are trying to apply a bilateral transformation on a nested queryset that won t work avoid circular import wrapped in parentheses to respect operator precedence do not call get_db_prep_lookup here as the value will be transformed before being used for lookup ensure expression is wrapped in parentheses to respect operator precedence but avoid double wrapping as it can be misinterpreted on some backends e g subqueries on sqlite oracle doesn t allow exists and filters to be compared to another expression unless they re wrapped in a case when wrap filters with a case when expression if a database backend e g oracle doesn t support boolean expression in select or group by list registerlookupmixin is first so that get_lookup and get_transform first examine self and then check output_field some lookups require field get_db_prep_value to be called on their inputs for relational fields use the target_field attribute of the output_field some lookups require field get_db_prep_value to be called on each value in an iterable an expression will be handled by the database but can coexist alongside real values rhs should be an iterable of values use batch_process_rhs to prepare transform those values the params list may contain expressions which compile to a sql param pair zip them to get sql and param pairs that refer to the same argument and attempt to replace them with the result of compiling the param step lookup defined by operators on postgresql avoid circular import avoid comparison against direct rhs if lhs is a boolean value that turns boolfield__exact true into where boolean_field instead of where boolean_field true when allowed allow floats to work as query values for integerfield without this the decimal portion of the float would always be discarded avoid circular import remove none from the list as null is never equal to anything unhashable items in self rhs rhs should be an iterable use batch_process_rhs to prepare transform those values this is a special case for databases which limit the number of elements which can appear in an in clause assume we are in startswith we need to produce sql like col like s thevalue for python values we can and should do that directly in python but if the value is for example reference to other column then we need to add the pattern match to the lookup by something like col like othercol so for python values we don t need any special pattern but for sql reference values or sql transformations we need the correct pattern added avoid the extract operation if the rhs is a direct value to allow indexes to be used skip the extract part by directly using the originating field that is self lhs lhs strip hyphens from a value when filtering a uuidfield on backends without a native datatype for uuid
import itertools
import math
import warnings

from django.core.exceptions import EmptyResultSet, FullResultSet
from django.db.backends.base.operations import BaseDatabaseOperations
from django.db.models.expressions import Case, Expression, Func, Value, When
from django.db.models.fields import (
    BooleanField,
    CharField,
    DateTimeField,
    Field,
    IntegerField,
    UUIDField,
)
from django.db.models.query_utils import RegisterLookupMixin
from django.utils.datastructures import OrderedSet
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.functional import cached_property
from django.utils.hashable import make_hashable


class Lookup(Expression):
    lookup_name = None
    prepare_rhs = True
    can_use_none_as_rhs = False

    def __init__(self, lhs, rhs):
        self.lhs, self.rhs = lhs, rhs
        self.rhs = self.get_prep_lookup()
        self.lhs = self.get_prep_lhs()
        if hasattr(self.lhs, "get_bilateral_transforms"):
            bilateral_transforms = self.lhs.get_bilateral_transforms()
        else:
            bilateral_transforms = []
        if bilateral_transforms:
            
            
            from django.db.models.sql.query import Query  

            if isinstance(rhs, Query):
                raise NotImplementedError(
                    "Bilateral transformations on nested querysets are not implemented."
                )
        self.bilateral_transforms = bilateral_transforms

    def apply_bilateral_transforms(self, value):
        for transform in self.bilateral_transforms:
            value = transform(value)
        return value

    def __repr__(self):
        return f"{self.__class__.__name__}({self.lhs!r}, {self.rhs!r})"

    def batch_process_rhs(self, compiler, connection, rhs=None):
        if rhs is None:
            rhs = self.rhs
        if self.bilateral_transforms:
            sqls, sqls_params = [], []
            for p in rhs:
                value = Value(p, output_field=self.lhs.output_field)
                value = self.apply_bilateral_transforms(value)
                value = value.resolve_expression(compiler.query)
                sql, sql_params = compiler.compile(value)
                sqls.append(sql)
                sqls_params.extend(sql_params)
        else:
            _, params = self.get_db_prep_lookup(rhs, connection)
            sqls, sqls_params = ["%s"] * len(params), params
        return sqls, sqls_params

    def get_source_expressions(self):
        if self.rhs_is_direct_value():
            return [self.lhs]
        return [self.lhs, self.rhs]

    def set_source_expressions(self, new_exprs):
        if len(new_exprs) == 1:
            self.lhs = new_exprs[0]
        else:
            self.lhs, self.rhs = new_exprs

    def get_prep_lookup(self):
        if not self.prepare_rhs or hasattr(self.rhs, "resolve_expression"):
            return self.rhs
        if hasattr(self.lhs, "output_field"):
            if hasattr(self.lhs.output_field, "get_prep_value"):
                return self.lhs.output_field.get_prep_value(self.rhs)
        elif self.rhs_is_direct_value():
            return Value(self.rhs)
        return self.rhs

    def get_prep_lhs(self):
        if hasattr(self.lhs, "resolve_expression"):
            return self.lhs
        return Value(self.lhs)

    def get_db_prep_lookup(self, value, connection):
        return ("%s", [value])

    def process_lhs(self, compiler, connection, lhs=None):
        lhs = lhs or self.lhs
        if hasattr(lhs, "resolve_expression"):
            lhs = lhs.resolve_expression(compiler.query)
        sql, params = compiler.compile(lhs)
        if isinstance(lhs, Lookup):
            
            sql = f"({sql})"
        return sql, params

    def process_rhs(self, compiler, connection):
        value = self.rhs
        if self.bilateral_transforms:
            if self.rhs_is_direct_value():
                
                
                value = Value(value, output_field=self.lhs.output_field)
            value = self.apply_bilateral_transforms(value)
            value = value.resolve_expression(compiler.query)
        if hasattr(value, "as_sql"):
            sql, params = compiler.compile(value)
            
            
            
            if sql and sql[0] != "(":
                sql = "(%s)" % sql
            return sql, params
        else:
            return self.get_db_prep_lookup(value, connection)

    def rhs_is_direct_value(self):
        return not hasattr(self.rhs, "as_sql")

    def get_group_by_cols(self):
        cols = []
        for source in self.get_source_expressions():
            cols.extend(source.get_group_by_cols())
        return cols

    def as_oracle(self, compiler, connection):
        
        
        wrapped = False
        exprs = []
        for expr in (self.lhs, self.rhs):
            if connection.ops.conditional_expression_supported_in_where_clause(expr):
                expr = Case(When(expr, then=True), default=False)
                wrapped = True
            exprs.append(expr)
        lookup = type(self)(*exprs) if wrapped else self
        return lookup.as_sql(compiler, connection)

    @cached_property
    def output_field(self):
        return BooleanField()

    @property
    def identity(self):
        return self.__class__, self.lhs, self.rhs

    def __eq__(self, other):
        if not isinstance(other, Lookup):
            return NotImplemented
        return self.identity == other.identity

    def __hash__(self):
        return hash(make_hashable(self.identity))

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        c = self.copy()
        c.is_summary = summarize
        c.lhs = self.lhs.resolve_expression(
            query, allow_joins, reuse, summarize, for_save
        )
        if hasattr(self.rhs, "resolve_expression"):
            c.rhs = self.rhs.resolve_expression(
                query, allow_joins, reuse, summarize, for_save
            )
        return c

    def select_format(self, compiler, sql, params):
        
        
        
        if not compiler.connection.features.supports_boolean_expr_in_select_clause:
            sql = f"CASE WHEN {sql} THEN 1 ELSE 0 END"
        return sql, params

    @cached_property
    def allowed_default(self):
        return self.lhs.allowed_default and self.rhs.allowed_default


class Transform(RegisterLookupMixin, Func):
    

    bilateral = False
    arity = 1

    @property
    def lhs(self):
        return self.get_source_expressions()[0]

    def get_bilateral_transforms(self):
        if hasattr(self.lhs, "get_bilateral_transforms"):
            bilateral_transforms = self.lhs.get_bilateral_transforms()
        else:
            bilateral_transforms = []
        if self.bilateral:
            bilateral_transforms.append(self.__class__)
        return bilateral_transforms


class BuiltinLookup(Lookup):
    def process_lhs(self, compiler, connection, lhs=None):
        lhs_sql, params = super().process_lhs(compiler, connection, lhs)
        field_internal_type = self.lhs.output_field.get_internal_type()
        if (
            hasattr(connection.ops.__class__, "field_cast_sql")
            and connection.ops.__class__.field_cast_sql
            is not BaseDatabaseOperations.field_cast_sql
        ):
            warnings.warn(
                (
                    "The usage of DatabaseOperations.field_cast_sql() is deprecated. "
                    "Implement DatabaseOperations.lookup_cast() instead."
                ),
                RemovedInDjango60Warning,
            )
            db_type = self.lhs.output_field.db_type(connection=connection)
            lhs_sql = (
                connection.ops.field_cast_sql(db_type, field_internal_type) % lhs_sql
            )
        lhs_sql = (
            connection.ops.lookup_cast(self.lookup_name, field_internal_type) % lhs_sql
        )
        return lhs_sql, list(params)

    def as_sql(self, compiler, connection):
        lhs_sql, params = self.process_lhs(compiler, connection)
        rhs_sql, rhs_params = self.process_rhs(compiler, connection)
        params.extend(rhs_params)
        rhs_sql = self.get_rhs_op(connection, rhs_sql)
        return "%s %s" % (lhs_sql, rhs_sql), params

    def get_rhs_op(self, connection, rhs):
        return connection.operators[self.lookup_name] % rhs


class FieldGetDbPrepValueMixin:
    

    get_db_prep_lookup_value_is_iterable = False

    def get_db_prep_lookup(self, value, connection):
        
        
        field = getattr(self.lhs.output_field, "target_field", None)
        get_db_prep_value = (
            getattr(field, "get_db_prep_value", None)
            or self.lhs.output_field.get_db_prep_value
        )
        return (
            "%s",
            [get_db_prep_value(v, connection, prepared=True) for v in value]
            if self.get_db_prep_lookup_value_is_iterable
            else [get_db_prep_value(value, connection, prepared=True)],
        )


class FieldGetDbPrepValueIterableMixin(FieldGetDbPrepValueMixin):
    

    get_db_prep_lookup_value_is_iterable = True

    def get_prep_lookup(self):
        if hasattr(self.rhs, "resolve_expression"):
            return self.rhs
        prepared_values = []
        for rhs_value in self.rhs:
            if hasattr(rhs_value, "resolve_expression"):
                
                
                pass
            elif self.prepare_rhs and hasattr(self.lhs.output_field, "get_prep_value"):
                rhs_value = self.lhs.output_field.get_prep_value(rhs_value)
            prepared_values.append(rhs_value)
        return prepared_values

    def process_rhs(self, compiler, connection):
        if self.rhs_is_direct_value():
            
            
            return self.batch_process_rhs(compiler, connection)
        else:
            return super().process_rhs(compiler, connection)

    def resolve_expression_parameter(self, compiler, connection, sql, param):
        params = [param]
        if hasattr(param, "resolve_expression"):
            param = param.resolve_expression(compiler.query)
        if hasattr(param, "as_sql"):
            sql, params = compiler.compile(param)
        return sql, params

    def batch_process_rhs(self, compiler, connection, rhs=None):
        pre_processed = super().batch_process_rhs(compiler, connection, rhs)
        
        
        
        
        sql, params = zip(
            *(
                self.resolve_expression_parameter(compiler, connection, sql, param)
                for sql, param in zip(*pre_processed)
            )
        )
        params = itertools.chain.from_iterable(params)
        return sql, tuple(params)


class PostgresOperatorLookup(Lookup):
    

    postgres_operator = None

    def as_postgresql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        params = tuple(lhs_params) + tuple(rhs_params)
        return "%s %s %s" % (lhs, self.postgres_operator, rhs), params


@Field.register_lookup
class Exact(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "exact"

    def get_prep_lookup(self):
        from django.db.models.sql.query import Query  

        if isinstance(self.rhs, Query):
            if self.rhs.has_limit_one():
                if not self.rhs.has_select_fields:
                    self.rhs.clear_select_clause()
                    self.rhs.add_fields(["pk"])
            else:
                raise ValueError(
                    "The QuerySet value for an exact lookup must be limited to "
                    "one result using slicing."
                )
        return super().get_prep_lookup()

    def as_sql(self, compiler, connection):
        
        
        
        if (
            isinstance(self.rhs, bool)
            and getattr(self.lhs, "conditional", False)
            and connection.ops.conditional_expression_supported_in_where_clause(
                self.lhs
            )
        ):
            lhs_sql, params = self.process_lhs(compiler, connection)
            template = "%s" if self.rhs else "NOT %s"
            return template % lhs_sql, params
        return super().as_sql(compiler, connection)


@Field.register_lookup
class IExact(BuiltinLookup):
    lookup_name = "iexact"
    prepare_rhs = False

    def process_rhs(self, qn, connection):
        rhs, params = super().process_rhs(qn, connection)
        if params:
            params[0] = connection.ops.prep_for_iexact_query(params[0])
        return rhs, params


@Field.register_lookup
class GreaterThan(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "gt"


@Field.register_lookup
class GreaterThanOrEqual(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "gte"


@Field.register_lookup
class LessThan(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "lt"


@Field.register_lookup
class LessThanOrEqual(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "lte"


class IntegerFieldOverflow:
    underflow_exception = EmptyResultSet
    overflow_exception = EmptyResultSet

    def process_rhs(self, compiler, connection):
        rhs = self.rhs
        if isinstance(rhs, int):
            field_internal_type = self.lhs.output_field.get_internal_type()
            min_value, max_value = connection.ops.integer_field_range(
                field_internal_type
            )
            if min_value is not None and rhs < min_value:
                raise self.underflow_exception
            if max_value is not None and rhs > max_value:
                raise self.overflow_exception
        return super().process_rhs(compiler, connection)


class IntegerFieldFloatRounding:
    

    def get_prep_lookup(self):
        if isinstance(self.rhs, float):
            self.rhs = math.ceil(self.rhs)
        return super().get_prep_lookup()


@IntegerField.register_lookup
class IntegerFieldExact(IntegerFieldOverflow, Exact):
    pass


@IntegerField.register_lookup
class IntegerGreaterThan(IntegerFieldOverflow, GreaterThan):
    underflow_exception = FullResultSet


@IntegerField.register_lookup
class IntegerGreaterThanOrEqual(
    IntegerFieldOverflow, IntegerFieldFloatRounding, GreaterThanOrEqual
):
    underflow_exception = FullResultSet


@IntegerField.register_lookup
class IntegerLessThan(IntegerFieldOverflow, IntegerFieldFloatRounding, LessThan):
    overflow_exception = FullResultSet


@IntegerField.register_lookup
class IntegerLessThanOrEqual(IntegerFieldOverflow, LessThanOrEqual):
    overflow_exception = FullResultSet


@Field.register_lookup
class In(FieldGetDbPrepValueIterableMixin, BuiltinLookup):
    lookup_name = "in"

    def get_refs(self):
        refs = super().get_refs()
        if self.rhs_is_direct_value():
            for rhs in self.rhs:
                if get_rhs_refs := getattr(rhs, "get_refs", None):
                    refs |= get_rhs_refs()
        return refs

    def get_prep_lookup(self):
        from django.db.models.sql.query import Query  

        if isinstance(self.rhs, Query):
            self.rhs.clear_ordering(clear_default=True)
            if not self.rhs.has_select_fields:
                self.rhs.clear_select_clause()
                self.rhs.add_fields(["pk"])
        return super().get_prep_lookup()

    def process_rhs(self, compiler, connection):
        db_rhs = getattr(self.rhs, "_db", None)
        if db_rhs is not None and db_rhs != connection.alias:
            raise ValueError(
                "Subqueries aren't allowed across different databases. Force "
                "the inner query to be evaluated using `list(inner_query)`."
            )

        if self.rhs_is_direct_value():
            
            try:
                rhs = OrderedSet(self.rhs)
                rhs.discard(None)
            except TypeError:  
                rhs = [r for r in self.rhs if r is not None]

            if not rhs:
                raise EmptyResultSet

            
            
            sqls, sqls_params = self.batch_process_rhs(compiler, connection, rhs)
            placeholder = "(" + ", ".join(sqls) + ")"
            return (placeholder, sqls_params)
        return super().process_rhs(compiler, connection)

    def get_rhs_op(self, connection, rhs):
        return "IN %s" % rhs

    def as_sql(self, compiler, connection):
        max_in_list_size = connection.ops.max_in_list_size()
        if (
            self.rhs_is_direct_value()
            and max_in_list_size
            and len(self.rhs) > max_in_list_size
        ):
            return self.split_parameter_list_as_sql(compiler, connection)
        return super().as_sql(compiler, connection)

    def split_parameter_list_as_sql(self, compiler, connection):
        
        
        max_in_list_size = connection.ops.max_in_list_size()
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.batch_process_rhs(compiler, connection)
        in_clause_elements = ["("]
        params = []
        for offset in range(0, len(rhs_params), max_in_list_size):
            if offset > 0:
                in_clause_elements.append(" OR ")
            in_clause_elements.append("%s IN (" % lhs)
            params.extend(lhs_params)
            sqls = rhs[offset : offset + max_in_list_size]
            sqls_params = rhs_params[offset : offset + max_in_list_size]
            param_group = ", ".join(sqls)
            in_clause_elements.append(param_group)
            in_clause_elements.append(")")
            params.extend(sqls_params)
        in_clause_elements.append(")")
        return "".join(in_clause_elements), params


class PatternLookup(BuiltinLookup):
    param_pattern = "%%%s%%"
    prepare_rhs = False

    def get_rhs_op(self, connection, rhs):
        
        
        
        
        
        
        
        
        
        if hasattr(self.rhs, "as_sql") or self.bilateral_transforms:
            pattern = connection.pattern_ops[self.lookup_name].format(
                connection.pattern_esc
            )
            return pattern.format(rhs)
        else:
            return super().get_rhs_op(connection, rhs)

    def process_rhs(self, qn, connection):
        rhs, params = super().process_rhs(qn, connection)
        if self.rhs_is_direct_value() and params and not self.bilateral_transforms:
            params[0] = self.param_pattern % connection.ops.prep_for_like_query(
                params[0]
            )
        return rhs, params


@Field.register_lookup
class Contains(PatternLookup):
    lookup_name = "contains"


@Field.register_lookup
class IContains(Contains):
    lookup_name = "icontains"


@Field.register_lookup
class StartsWith(PatternLookup):
    lookup_name = "startswith"
    param_pattern = "%s%%"


@Field.register_lookup
class IStartsWith(StartsWith):
    lookup_name = "istartswith"


@Field.register_lookup
class EndsWith(PatternLookup):
    lookup_name = "endswith"
    param_pattern = "%%%s"


@Field.register_lookup
class IEndsWith(EndsWith):
    lookup_name = "iendswith"


@Field.register_lookup
class Range(FieldGetDbPrepValueIterableMixin, BuiltinLookup):
    lookup_name = "range"

    def get_rhs_op(self, connection, rhs):
        return "BETWEEN %s AND %s" % (rhs[0], rhs[1])


@Field.register_lookup
class IsNull(BuiltinLookup):
    lookup_name = "isnull"
    prepare_rhs = False

    def as_sql(self, compiler, connection):
        if not isinstance(self.rhs, bool):
            raise ValueError(
                "The QuerySet value for an isnull lookup must be True or False."
            )
        if isinstance(self.lhs, Value):
            if self.lhs.value is None or (
                self.lhs.value == ""
                and connection.features.interprets_empty_strings_as_nulls
            ):
                result_exception = FullResultSet if self.rhs else EmptyResultSet
            else:
                result_exception = EmptyResultSet if self.rhs else FullResultSet
            raise result_exception
        sql, params = self.process_lhs(compiler, connection)
        if self.rhs:
            return "%s IS NULL" % sql, params
        else:
            return "%s IS NOT NULL" % sql, params


@Field.register_lookup
class Regex(BuiltinLookup):
    lookup_name = "regex"
    prepare_rhs = False

    def as_sql(self, compiler, connection):
        if self.lookup_name in connection.operators:
            return super().as_sql(compiler, connection)
        else:
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            sql_template = connection.ops.regex_lookup(self.lookup_name)
            return sql_template % (lhs, rhs), lhs_params + rhs_params


@Field.register_lookup
class IRegex(Regex):
    lookup_name = "iregex"


class YearLookup(Lookup):
    def year_lookup_bounds(self, connection, year):
        from django.db.models.functions import ExtractIsoYear

        iso_year = isinstance(self.lhs, ExtractIsoYear)
        output_field = self.lhs.lhs.output_field
        if isinstance(output_field, DateTimeField):
            bounds = connection.ops.year_lookup_bounds_for_datetime_field(
                year,
                iso_year=iso_year,
            )
        else:
            bounds = connection.ops.year_lookup_bounds_for_date_field(
                year,
                iso_year=iso_year,
            )
        return bounds

    def as_sql(self, compiler, connection):
        
        
        if self.rhs_is_direct_value():
            
            
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            rhs_sql, _ = self.process_rhs(compiler, connection)
            rhs_sql = self.get_direct_rhs_sql(connection, rhs_sql)
            start, finish = self.year_lookup_bounds(connection, self.rhs)
            params.extend(self.get_bound_params(start, finish))
            return "%s %s" % (lhs_sql, rhs_sql), params
        return super().as_sql(compiler, connection)

    def get_direct_rhs_sql(self, connection, rhs):
        return connection.operators[self.lookup_name] % rhs

    def get_bound_params(self, start, finish):
        raise NotImplementedError(
            "subclasses of YearLookup must provide a get_bound_params() method"
        )


class YearExact(YearLookup, Exact):
    def get_direct_rhs_sql(self, connection, rhs):
        return "BETWEEN %s AND %s"

    def get_bound_params(self, start, finish):
        return (start, finish)


class YearGt(YearLookup, GreaterThan):
    def get_bound_params(self, start, finish):
        return (finish,)


class YearGte(YearLookup, GreaterThanOrEqual):
    def get_bound_params(self, start, finish):
        return (start,)


class YearLt(YearLookup, LessThan):
    def get_bound_params(self, start, finish):
        return (start,)


class YearLte(YearLookup, LessThanOrEqual):
    def get_bound_params(self, start, finish):
        return (finish,)


class UUIDTextMixin:
    

    def process_rhs(self, qn, connection):
        if not connection.features.has_native_uuid_field:
            from django.db.models.functions import Replace

            if self.rhs_is_direct_value():
                self.rhs = Value(self.rhs)
            self.rhs = Replace(
                self.rhs, Value("-"), Value(""), output_field=CharField()
            )
        rhs, params = super().process_rhs(qn, connection)
        return rhs, params


@UUIDField.register_lookup
class UUIDIExact(UUIDTextMixin, IExact):
    pass


@UUIDField.register_lookup
class UUIDContains(UUIDTextMixin, Contains):
    pass


@UUIDField.register_lookup
class UUIDIContains(UUIDTextMixin, IContains):
    pass


@UUIDField.register_lookup
class UUIDStartsWith(UUIDTextMixin, StartsWith):
    pass


@UUIDField.register_lookup
class UUIDIStartsWith(UUIDTextMixin, IStartsWith):
    pass


@UUIDField.register_lookup
class UUIDEndsWith(UUIDTextMixin, EndsWith):
    pass


@UUIDField.register_lookup
class UUIDIEndsWith(UUIDTextMixin, IEndsWith):
    pass