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
Refs #33482 -- Fixed QuerySet selectin and filtering againts negated Exists() with empty queryset. #15407
Conversation
django/db/models/expressions.py
Outdated
return ( | ||
"1=1" | ||
if not features.supports_boolean_expr_in_select_clause | ||
else "", | ||
(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange, I thought that sql=''
translated to an elided expression.
django/django/db/models/sql/where.py
Lines 85 to 87 in 25514b6
if sql: | |
result.append(sql) | |
result_params.extend(params) |
Here's the query I get from expressions.tests.ExistsTests.test_negated_empty_exists
from non-Oracle backends
SELECT "expressions_manager"."id", "expressions_manager"."name" FROM "expressions_manager" WHERE "expressions_manager"."id" = 1
Do we need to adapt select_format
to special case sql = ""
? Probably, but I didn't think that select_format
would be involved in this kind of query?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but we have a different select_format
on Oracle: "CASE WHEN {} THEN 1 ELSE 0 END".format(sql)
, so an empty string is translated to CASE WHEN THEN 1 ELSE 0 END
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should adapt select_format
in this case to handle ''
to encapsulate the Oracle logic in one location.
It's not clear why select_format
is called here though as we're directly filtering against the expression and not selecting it?
django/tests/expressions/tests.py
Lines 1916 to 1918 in 25514b6
qs = Manager.objects.filter( | |
~Exists(Manager.objects.none()) & Q(pk=manager.pk) | |
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, sorry, it's not about select_format()
🤦 Exists()
is wrapped with Case()
also in the WHERE
clause:
django/django/db/models/lookups.py
Lines 136 to 140 in 25514b6
for expr in (self.lhs, self.rhs): | |
if connection.ops.conditional_expression_supported_in_where_clause(expr): | |
expr = Case(When(expr, then=True), default=False) | |
wrapped = True | |
exprs.append(expr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh I see, ok let's go with this approach for now. There's likely a good way to reconcile the multiple boolean expression handling we have for Oracle across the ORM but this simple change should go a long way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking back at ticket-33482 I think that a better way forward would be to have it drop its custom support for negation and have __invert__
simply return ~Q(self)
as the latter already has all the proper machinery to deal with negation.
This adjustment should do for now though!
aebb16b
to
4cdcc03
Compare
@charettes Thanks for checking 👍 I added an extra test for selecting negated |
buildbot, test on oracle. |
@charettes It seems that I found a regression in selecting negated |
4cdcc03
to
62fdf67
Compare
Ah I assume something along |
…Exists() with empty queryset. Regression in b7d1da5.
62fdf67
to
ecbb1d7
Compare
Yes :) |
Regression in b7d1da5.