8000 Fixed #36085 -- Added JSONField support for negative array indexing on SQLite. by savanto · Pull Request #19030 · django/django · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fixed #36085 -- Added JSONField support for negative array indexing on SQLite. #19030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 14, 2025

Conversation

savanto
Copy link
Contributor
@savanto savanto commented Jan 11, 2025

Trac ticket number

ticket-36085

Branch description

The SQLite backend has special syntax for performing negative-indexing in jsonfields that is different from other backends: negative indices must be prepended by a literal # character.

Refactor

This change includes a refactor to turn the function django.db.models.fields.json.compile_json_path into a method of [Base]DatabaseOperations, to allow backend-specific customization. The refactor is cherry-picked from #19182, cleaned up, and modified to reduce duplicated code when customizing backend behavior.

Checklist

  • This PR targets the main branch.
  • The commit message is written in past tense, mentions the ticket number, and ends with a period.
  • I have checked the "Has patch" ticket flag in the Trac system.
  • I have added or updated relevant tests.
  • I have added or updated relevant docs, including release notes if applicable.
  • I have attached screenshots in both light and dark modes for any UI changes.

Copy link
@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Hello! Thank you for your contribution 💪

As it's your first contribution be sure to check out the patch review checklist.

If you're fixing a ticket from Trac make sure to set the "Has patch" flag and include a link to this PR in the ticket!

If you have any design or process questions then you can ask in the Django forum.

Welcome aboard ⛵️!

@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch 2 times, most recently from 7f48874 to e5bb2d4 Compare January 11, 2025 19:51
@savanto savanto marked this pull request as draft January 11, 2025 23:21
@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch 2 times, most recently from 8b0ed71 to 8550319 Compare January 12, 2025 07:44
@savanto savanto marked this pull request as ready for review January 12, 2025 19:49
@@ -152,7 +152,10 @@ def compile_json_path(key_transforms, include_root=True):
path.append(".")
path.append(json.dumps(key_transform))
else:
path.append("[%s]" % num)
if connection.vendor == "sqlite" and num < 0:
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather delegate this to a DatabaseOperations method in case other database backends also need this hook. In fact, compile_json_path()might be a candidate to delegate to the database backend. The snowflake backend needed to override it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@timgraham I'm fairly confident I can move compile_json_path into [Base]DatabaseOperations, but I'm not sure whether this would be in scope for this PR. I could

  1. make it part of this PR
  2. modify this PR to only delegate the "numerical append" operation to DatabaseOperations, and leave delegation/migration of the whole function to a separate PR & ticket
  3. make a separate PR & ticket to migrate the whole function, then rebase this PR on top

I'm not that familiar with Django development conventions in this regard, but I'm happy to do either of the above, or another suggestion. Please advise!

Copy link
Contributor

Choose a reason for hiding this comment

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

Now that compile_json_path is backend-specific, I think it makes sense to move it to DatabaseOperations. I'm not sure if it has to be a separate PR, but it definitely has to be in a separate commit.

I'd suggest keeping it in the same PR for now so it's easier to read the refactoring in context, but keep it in a separate commit so we can split the PR later if needed.

Though, if we only extract the whole compile_json_path to DatabaseOperations and not separate the "numerical" part to its own operation, we'll have to duplicate most of the logic for SQLite. So perhaps split the function into two operation methods? (I'm not sure myself...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good to me! I must have misunderstood the Django guidelines -- I thought that each PR must be a single commit. I will do the refactor work here, and ping you when it's ready for review.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think eventually it will need to be separate PRs. It's just that for refactoring we generally need to build a case for it and have an agreement before it can be accepted. Having the context in the same PR helps other reviewers understand the rationale for refactoring.

If you're happy with creating a new PR just for the refactoring and have this PR based on top of that one, I'm sure it's fine too. But as you incorporate any feedback in the refactoring PR, you'll have to keep this one in sync, which can be a bit more work compared to have just the one PR and split it when we're happy.

Copy link
Member

Choose a reason for hiding this comment

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

Refactoring (with multiple commits) is sometimes included in the same PR as a feature. See #19073 for an example. I doubt there would be a need to split it unless it gets quite unwieldy to review.

@savanto savanto marked this pull request as draft January 13, 2025 16:37
@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch 2 times, most recently from dc6326e to 86f834b Compare March 17, 2025 19:02
@savanto
Copy link
Contributor Author
savanto commented Mar 17, 2025

Thanks everyone for your inputs! I sat on this for a while because I wasn't sure whether development was going to continue in #19182 or not, but I decided to finish it up here when that PR was closed.

Thanks to @Hesham942 for taking the first shot at the suggested refactor. I took the liberty of cherry-picking the refactor commits from #19182 so that your contribution would appear in this PR. On top of that, I made the following changes:

  • Finished moving compile_json_path function fully into BaseDatabaseOperations.compile_json_path, updated all call sites, and removed the old function completely.
  • In order to avoid duplicating most of compile_json_path for backends deviating from super, I split out the special numerical indexing logic into its own DatabaseOperations hook per @laymonage's advice.
  • Fixed the actual issue at hand, ticket-36085, by overriding just the numerical indexing logic for the SQLite backend.
  • I had previously added some unit tests for testing negative indexing, but the tests failed when running on the MySQL backend in Jenkins. I added a backend feature flag per this suggestion from @timgraham to exclude these tests from running on MySQL. Note that the refactor and SQLite-specific fix do not affect MySQL (behavior should be exactly the same as before), so I felt it was safe to exclude MySQL. I'm not actually sure whether MySQL could support negative indexing in JSON paths, and whether a backend-specific fix is necessary to do so just like the fix for SQLite in this PR. However, implementing support for negative indexing on MySQL is out of scope for this PR.

Next steps

I believe that the compile_json_path -> BaseDatabaseOperations.compile_json_path refactor is fully encapsulated in 3599870.

The changes pertaining just to fixing ticket-36085 are spread across 138122b & d9c1252 (tests & feature flag) and 86f834b (SQLite-specific fix).

We discussed keeping the changes together in one PR, or splitting them up into multiple ones. @timgraham & @laymonage I'm happy to do either as convenient/appropriate for Django, please let me know your thoughts on this.

@savanto savanto marked this pull request as ready for review March 17, 2025 20:49
@savanto savanto requested review from timgraham and laymonage March 17, 2025 20:49
@Hesham942
Copy link
Contributor

Thanks everyone for your inputs! I sat on this for a while because I wasn't sure whether development was going to continue in #19182 or not, but I decided to finish it up here when that PR was closed.

Thanks to @Hesham942 for taking the first shot at the suggested refactor. I took the liberty of cherry-picking the refactor commits from #19182 so that your contribution would appear in this PR. On top of that, I made the following changes:

Thank you so much, @savanto! I really appreciate your understanding and for acknowledging my contribution. That means a lot! 🌟

@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch 3 times, most recently from 4704507 to 34dd616 Compare March 21, 2025 19:02
@savanto savanto requested a review from sarahboyce March 29, 2025 22:44
@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch from 69ebd5f to b60d89e Compare April 18, 2025 21:00
@savanto
Copy link
Contributor Author
savanto commented Apr 19, 2025

Fixed merge conflicts.

Copy link
Contributor
@laymonage laymonage left a comment

Choose a reason for hiding this comment

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

I only have a few suggestions to the wording (mostly to avoid confusion with database indexes), but otherwise I'm very happy with the code changes here. Thanks for continuing to work on this!

@savanto
Copy link
Contributor Author
savanto commented Apr 25, 2025

Thanks @laymonage ! I've applied your suggested wording.

@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch from a7018d1 to 58bb175 Compare April 25, 2025 16:43
Copy link
Contributor
@laymonage laymonage left a comment

Choose a reason for hiding this comment

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

Thanks, this looks good to me! (The commits will need to be squashed/reorganized though)

P.s. native English speakers might want to make further suggestions to the wording.

Copy link
Contributor
@sarahboyce sarahboyce left a comment

Choose a reason for hiding this comment

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

Thank you @salvanto

.. admonition:: MySQL and MariaDB

Using negative JSON array indices is not currently supported.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a .. versionchanged:: 6.0 note for this and a release note for 6.0?

@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch from ce42c50 to f58ea27 Compare April 29, 2025 19:08
@savanto savanto requested a review from sarahboyce April 29, 2025 20:36
@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch from f58ea27 to 0306dcd Compare May 3, 2025 16:55
@sarahboyce sarahboyce force-pushed the sqlite-negative-index-jsonfield branch from 0306dcd to f204a62 Compare May 13, 2025 11:43
Copy link
Contributor
@sarahboyce sarahboyce left a comment

Choose a reason for hiding this comment

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

Thank you, I pushed minor tweaks 👍

@sarahboyce sarahboyce force-pushed the sqlite-negative-index-jsonfield branch from f204a62 to b22427b Compare May 13, 2025 11:55
@sarahboyce
Copy link
Contributor

buildbot, test on oracle.

Copy link
Contributor
@sarahboyce sarahboyce left a comment

Choose a reason for hiding this comment

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

There are some failures on Oracle, either these need fixing or supports_json_negative_indexing should be set to False for Oracle and it should be added to the admonition 👍

@savanto savanto force-pushed the sqlite-negative-index-jsonfield branch 2 times, most recently from f54991c to e9930d1 Compare May 13, 2025 20:07
@savanto
Copy link
Contributor Author
savanto commented May 13, 2025

buildbot, test on oracle.

@sarahboyce sarahboyce force-pushed the sqlite-negative-index-jsonfield branch from e9930d1 to 86469dc Compare May 14, 2025 06:27
@sarahboyce
Copy link
Contributor

buildbot, test on oracle.

@sarahboyce sarahboyce changed the title Fixed #36085 -- negative indexing jsonfield array syntax for SQLite. Fixed #36085 -- Added JSONField support for negative array indexing on SQLite. May 14, 2025
@sarahboyce sarahboyce merged commit 8620a3b into django:main May 14, 2025
15 of 35 checks passed
@savanto savanto deleted the sqlite-negative-index-jsonfield branch May 14, 2025 12:40
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.

6 participants
0