Repository: django/django
File: django/db/models/fields/json.py (Python)

Code before:
    case-sensitive.
    """

    def process_lhs(self, compiler, connection):
        lhs, lhs_params = super().process_lhs(compiler, connection)
        if connection.vendor == "mysql":
            return "LOWER(%s)" % lhs, lhs_params
        return lhs, lhs_params

    def process_rhs(self, compiler, connection):
        rhs, rhs_params = super().process_rhs(compiler, connection)
        if connection.vendor == "mysql":
            return "LOWER(%s)" % rhs, rhs_params
        return rhs, rhs_params


class JSONExact(lookups.Exact):
    # RemovedInDjango70Warning: When the deprecation period is over, remove
    # the following line.
    can_use_none_as_rhs = True

    def process_rhs(self, compiler, connection):
        if self.rhs is None and isinstance(self.lhs, expressions.Col):
            warnings.warn(
                "Using None as the right-hand side of an exact lookup on JSONField "
                "to mean JSON scalar 'null' is deprecated. Use JSONNull() instead.",
                RemovedInDjango70Warning,
                stacklevel=2,
            )

        rhs, rhs_params = super().process_rhs(compiler, connection)

        # RemovedInDjango70Warning: When the deprecation period is over, remove
        # The following if-block entirely.
        # Treat None lookup values as null.
        if rhs == "%s" and rhs_params == [None]:
            rhs_params = ["null"]
        if connection.vendor == "mysql" and not isinstance(self.rhs, JSONNull):
            func = ["JSON_EXTRACT(%s, '$')"] * len(rhs_params)
            rhs %= tuple(func)
        return rhs, rhs_params

    def as_oracle(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        if connection.features.supports_primitives_in_json_field:
            lhs = f"JSON({lhs})"
            rhs = f"JSON({rhs})"
        return f"JSON_EQUAL({lhs}, {rhs} ERROR ON ERROR)", (*lhs_params, *rhs_params)



Review comment (suggestion):
I think there will be enough unsuspecting users that weren't trying to select on top-level scalar null that we could also point users toward `__isnull=True` for key or index absence.

Code after:
    case-sensitive.
    """

    def process_lhs(self, compiler, connection):
        lhs, lhs_params = super().process_lhs(compiler, connection)
        if connection.vendor == "mysql":
            return "LOWER(%s)" % lhs, lhs_params
        return lhs, lhs_params

    def process_rhs(self, compiler, connection):
        rhs, rhs_params = super().process_rhs(compiler, connection)
        if connection.vendor == "mysql":
            return "LOWER(%s)" % rhs, rhs_params
        return rhs, rhs_params


class JSONExact(lookups.Exact):
    # RemovedInDjango70Warning: When the deprecation period is over, remove
    # the following line.
    can_use_none_as_rhs = True

    def process_rhs(self, compiler, connection):
        if self.rhs is None and not isinstance(self.lhs, KeyTransform):
            warnings.warn(
                "Using None as the right-hand side of an exact lookup on JSONField to "
                "mean JSON scalar 'null' is deprecated. Use JSONNull() instead (or use "
                "the __isnull lookup if you meant SQL NULL).",
                RemovedInDjango70Warning,
                skip_file_prefixes=django_file_prefixes(),
            )

        rhs, rhs_params = super().process_rhs(compiler, connection)

        # RemovedInDjango70Warning: When the deprecation period is over, remove
        # The following if-block entirely.
        # Treat None lookup values as null.
        if rhs == "%s" and (*rhs_params,) == (None,):
            rhs_params = ("null",)
        if connection.vendor == "mysql" and not isinstance(self.rhs, JSONNull):
            func = ["JSON_EXTRACT(%s, '$')"] * len(rhs_params)
            rhs %= tuple(func)
        return rhs, rhs_params

    def as_oracle(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        if connection.features.supports_primitives_in_json_field:
            lhs = f"JSON({lhs})"
            rhs = f"JSON({rhs})"
        return f"JSON_EQUAL({lhs}, {rhs} ERROR ON ERROR)", (*lhs_params, *rhs_params)