Opened 4 years ago
Closed 4 years ago
#32231 closed Bug (fixed)
It should be possible to pass None as params for Model.objects.raw
Reported by: | Alexander Lyabah | Owned by: | Alexander Lyabah |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | raw, psycopg2, execute, orm, db |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I was playing with:
- psycopg2==2.8.6
- last django 2.2 (-e git+git@…:django/django.git@e893c0ad8b0b5b0a1e5be3345c287044868effc4#egg=Django)
Model.objects.raw has raw_query and params=None attributes, as well as cursor.execute has the same attributes, and raw
will eventually calls execute
, since raw
is more low-level way of using DB.
But, params=None of raw
function doesn't pass as None into execute
function, but empty tuple instead.
The problem here is that, psycopg2 treats differently params=None and params=(), and it is impossible to pass None using raw
function.
The simple example can be found in test from patch
query = "SELECT * FROM raw_query_author WHERE first_name like 'J%'"
qset = Author.objects.raw(query)
results = list(qset)
print(len(results))
in current Django version (I was using 2.2) it raises an exception from psycopg2
The attached patch allows using None in params
Attachments (1)
Change History (10)
comment:1 by , 4 years ago
comment:2 by , 4 years ago
From the Executing custom SQL directly section of the Performing raw SQL queries documentation
Note that if you want to include literal percent signs in the query, you have to double them in the case you are passing parameters:
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])
So it seems that it was meant to work the way described above when no parameters are provided but it was never tested to do so and might have regressed at some point?
We should determine for how long the implementation has diverged from the documentation to take an informed decision here as some users might have simply worked around this limitation by always doubling percentage signs even when no parameters are provided over the years. If we were to simply switch back to the documented way of doing things we could silently break queries of the form
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%'")
I think we either need to go through a deprecation period where params=()
keeps being passed if '%%' in sql
or bite the bullet and adjust the documentation accordingly.
by , 4 years ago
Attachment: | params_none.diff added |
---|
comment:3 by , 4 years ago
I update the patch for backward compatibility
comment:4 by , 4 years ago
Triage Stage: | Unreviewed → Accepted |
---|---|
Version: | 2.2 → master |
To amend what I said above, cursor
works fine, it's only raw
that doesn't so the documentation divergence seems moot.
Allowing params=None
to be explicitly specified to opt-in this behaviour like the reporter suggested seems like an acceptable compromise.
comment:5 by , 4 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:6 by , 4 years ago
Needs documentation: | set |
---|---|
Patch needs improvement: | set |
comment:7 by , 4 years ago
Needs documentation: | unset |
---|---|
Patch needs improvement: | unset |
Triage Stage: | Accepted → Ready for checkin |
Not sure I understand the rationale here
Doing something along
Is prone to SQL injection assuming
'J%'
could be coming from user input, you definitely want to be doing the following instead