Skip to content

Fixed #33552 -- Fixed JSONField has key lookups with numeric keys on MariaDB, MySQL, Oracle, and SQLite. - #15503

Merged
felixxm merged 1 commit into
django:mainfrom
laymonage:ticket_33552
Mar 15, 2022
Merged

felixxm merged 1 commit into
django:mainfrom
laymonage:ticket_33552

Conversation

@laymonage

Copy link
Copy Markdown
Member

Fixes ticket-33552.

Doesn't seem very sound, but it's tricky because the HasKey lookup is used by other lookups (isnull and exact). It's the best I can come up with for now.

@felixxm felixxm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@laymonage Thanks for this patch 👍 Unfortunately it makes this code less readable. What do you think about adding HasKeyLookup.compile_json_path() that could be overridden in a new subclass e.g. HasKeyOrArrayIndex used in KeyTransformIsNull and KeyTransformExact on SQLite and Oracle? For example (not tested draft):

class HasKeyLookup(PostgresOperatorLookup):
    def compile_json_path(self, key_transforms, include_root=True):
        path = ["$"] if include_root else []
        for key_transform in key_transforms:
            path.append(".")
            path.append(json.dumps(key_transform))
        return "".join(path)
    ...

class HasKeyOrArrayIndex(HasKey):
    def compile_json_path(self, key_transforms, include_root=True):
        return compile_json_path(self, key_transforms, include_root)

...

class KeyTransformIsNull(lookups.IsNull):
    # key__isnull=False is the same as has_key='key'
    def as_oracle(self, compiler, connection):
        sql, params = HasKeyOrArrayIndex(
            self.lhs.lhs,
            self.lhs.key_name
        ).as_oracle(compiler, connection)
        ...

    def as_sqlite(self, compiler, connection):
        template = "JSON_TYPE(%s, %%s) IS NULL"
        if not self.rhs:
            template = "JSON_TYPE(%s, %%s) IS NOT NULL"
        return HasKeyOrArrayIndex(self.lhs.lhs, self.lhs.key_name).as_sql(
            compiler,
            connection,
            template=template,
        )

@laymonage
laymonage force-pushed the ticket_33552 branch 3 times, most recently from ae19b51 to 01dbbe2 Compare March 15, 2022 01:11
@laymonage

laymonage commented Mar 15, 2022 •

Copy link
Copy Markdown
Member Author

@laymonage Thanks for this patch 👍 Unfortunately it makes this code less readable. What do you think about adding HasKeyLookup.compile_json_path() that could be overridden in a new subclass e.g. HasKeyOrArrayIndex used in KeyTransformIsNull and KeyTransformExact on SQLite and Oracle?

That's much cleaner, thanks! I've made those changes. It's a bit ambiguous to have compile_json_path methods inside and outside the class, though, but I guess it's fine as this is just an implementation detail.

@felixxm

felixxm commented Mar 15, 2022

Copy link
Copy Markdown
Member

@laymonage Thanks 👍

I noticed that we need to change the current behavior only for the final key, so I renamed HasKeyLookup.compile_json_path() to HasKeyLookup.compile_json_path_final_key(), removed an internal loop and the include_root argument.

@felixxm felixxm changed the title Fixed #33552 -- Fixed HasKeyLookup with numeric keys on SQLite, MySQL, MariaDB, and Oracle. Fixed #33552 -- Fixed JSONField has key lookups with numeric keys on MariaDB, MySQL, Oracle, and SQLite. Mar 15, 2022
@felixxm
felixxm merged commit a88fab1 into django:main Mar 15, 2022
calio added a commit to calio/swe.swebench_gva.django that referenced this pull request Nov 21, 2025
…ield lookups to handle numeric keys on SQLite, MySQL, and Oracle.

The issue was that numeric string keys like '1111' were being treated as array indices instead of object keys. The fix:

1. Added try_parse_int parameter to compile_json_path() to control whether numeric strings should be parsed as integers
2. Modified HasKeyLookup to use try_parse_int=False for plain key values (treating numeric strings as object keys)
3. Modified KeyTransform to preserve the original type of keys (integers vs strings)
4. Modified KeyTransformFactory to parse numeric strings as integers for array access
5. Updated HasKeys.get_prep_lookup() to preserve key types instead of converting all to strings

This ensures that:
- Array indices like value__d__0 correctly use [0] syntax
- Numeric string keys like value__has_key='1111' correctly use ."1111" syntax
- The lookups work correctly on SQLite, MySQL, and Oracle databases
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants