Skip to content
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

Fixes/sqlite3 too many sql variables #6749

Conversation

jrief
Copy link
Contributor

@jrief jrief commented Oct 21, 2019

Summary

Fixes #6746
Ported from #6748 for django-CMS 3.7

Proposed changes in this pull request

Fixes the bug.

General checklist

  • I have updated the CHANGELOG.txt
  • I have created backports if necessary
  • I have updated the documentation and/or amended the upgrade section
    if necessary

@coveralls
Copy link

Coverage Status

Coverage remained the same at 78.215% when pulling 28c97e8 on jrief:fixes/sqlite3-too-many-SQL-variables into 317ee96 on divio:release/3.7.x.

@jrief
Copy link
Contributor Author

jrief commented Oct 21, 2019

Additional explanation:
In function get_page_actions_for_user(...), a long list of PKs is generated. This list then is used to generate a filter query checking which page is inside that list.

For SQLite, the Django ORM creates SQL using a ? as placeholder. In SQLite this number of placeholders is limited to 999, hence not more than 999 pages can be descendants of another page. By building chunks of 500, this problem can be avoided.

@@ -188,32 +188,38 @@ def get_page_actions_for_user(user, site):
)
nodes = [page.node for page in pages]
pages_by_id = {}
page_permissions_chunks = []

for offset in range(0, pages.count(), 500):
Copy link
Member

Choose a reason for hiding this comment

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

not a fan of having a magical number in here. As it only affects SQLite we should also probably target only SQLite with such an exception. I'm sadly not deep into the permission system by itself. WDYT @yakky or @czpython ?

Copy link
Member

Choose a reason for hiding this comment

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

I will make this number more explicit by setting a constant (something like PAGE_PERMISSION_CHUNK_SIZE)
We can then have different numbers for different database backends and make it very large (to the point of being infinite) for DBMS other than sqlite

@FinalAngel FinalAngel requested a review from yakky November 7, 2019 09:25
@FinalAngel FinalAngel added this to the 3.8.x milestone Nov 7, 2019
@jrief
Copy link
Contributor Author

jrief commented Nov 19, 2019

@FinalAngel , @yakky
This limit is hard-coded into SQLite itself using SQLITE_MAX_VARIABLE_NUMBER. Changing it requires the recompilation of the SQLite code. Unfortunately I haven't found any interface to fetch that number using some sort of SQLite dialect. 500 is certainly a safe lower limit which helps to prevent these sorts of errors.

Proposal:

a) I make this number MAX_INT for non-SQLite databases and 500 for SQLite. Making this configurable, in my opinion doesn't make much sense, since rarely anybody knows that limit.
b) Through the Page-model, I check on which database it is running, in order to answer a).

Copy link
Member

@yakky yakky left a comment

Choose a reason for hiding this comment

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

@jrief do you think we can have a test to replicate this behavior? It will probably be very slow (due to the huge number of pages needed) and thus it might be needed to be marked with some skipIf to only run in a subset of the travis matrix

@czpython
Copy link
Contributor

czpython commented Jan 2, 2020

I honestly don't think this is the way to go, for being a limitation of sqlite only, worst-case scenario I would add a condition that avoids this issue only for sqlite.
A more involved solution would be to refactor this into subqueries instead of manual ids.

@jrief
Copy link
Contributor Author

jrief commented Jan 4, 2020

@yakky puh, writing a test for this scenario would indeed require to add a lot of pages to the tree. I'm not sure if this is worthwhile.

something like PAGE_PERMISSION_CHUNK_SIZE

This is perfectly fine, but that number shall not be configurable through the owners settings.py

@czpython

I would add a condition that avoids this issue only for sqlite.

That's simple. We can use a variable instead of 500 and change that to MAX_INT for non-sqlite database.

A more involved solution would be to refactor this into subqueries instead of manual ids.

I looked at this, but since the queryset which could be used for the subquery, is modified by calling some Django permission functions, I don't believe it's feasible.

Shall I modify that pull request and use a meaningful variable name instead of 500 and apply that only to SQLite?

@yakky
Copy link
Member

yakky commented Jan 4, 2020

@jrief
agree PAGE_PERMISSION_CHUNK_SIZE is just an internal constant, not a setting.
As you wrote is set to something like:

if driver == "sqlite3":
   PAGE_PERMISSION_CHUNK_SIZE = 500
else:
   PAGE_PERMISSION_CHUNK_SIZE = MAX_INT

Subqueries would be great, and the with_user call can be probably rewritten, but I don't think it's worth the effort, unless we see some global improvement

@goutnet
Copy link
Contributor

goutnet commented May 19, 2021

@jrief would this be useful in develop for 3.9.0 ?

@goutnet goutnet modified the milestones: 3.9.0, 3.9.x May 19, 2021
@stale
Copy link

stale bot commented Jun 30, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Jun 30, 2022
@stale
Copy link

stale bot commented Jul 28, 2022

This will now be closed due to inactivity, but feel free to reopen it.

@stale stale bot closed this Jul 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants