summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
Diffstat (limited to 'django')
-rw-r--r--django/db/backends/base/features.py3
-rw-r--r--django/db/backends/base/operations.py25
-rw-r--r--django/db/backends/mysql/operations.py14
-rw-r--r--django/db/backends/oracle/operations.py24
-rw-r--r--django/db/backends/postgresql/features.py1
-rw-r--r--django/db/models/lookups.py15
6 files changed, 36 insertions, 46 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py
index 6770c177c1..c9e78b5746 100644
--- a/django/db/backends/base/features.py
+++ b/django/db/backends/base/features.py
@@ -78,6 +78,9 @@ class BaseDatabaseFeatures:
# Does the backend ignore unnecessary ORDER BY clauses in subqueries?
ignores_unnecessary_order_by_in_subqueries = True
+ # Is there a true datatype for boolean?
+ has_native_boolean_field = False
+
# Is there a true datatype for uuid?
has_native_uuid_field = False
diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py
index 29ef7d93d1..db45d6922e 100644
--- a/django/db/backends/base/operations.py
+++ b/django/db/backends/base/operations.py
@@ -8,8 +8,10 @@ import sqlparse
from django.conf import settings
from django.db import NotSupportedError, models, transaction
-from django.db.models.expressions import Col
+from django.db.models import Exists, ExpressionWrapper, Lookup
+from django.db.models.expressions import Col, RawSQL
from django.db.models.fields.composite import CompositePrimaryKey
+from django.db.models.sql.where import WhereNode
from django.utils import timezone
from django.utils.duration import duration_microseconds
from django.utils.encoding import force_str
@@ -716,10 +718,25 @@ class BaseDatabaseOperations:
def conditional_expression_supported_in_where_clause(self, expression):
"""
- Return True, if the conditional expression is supported in the WHERE
- clause.
+ Return True, if the conditional expression is directly supported in the
+ WHERE clause.
"""
- return True
+ # If the backend supports native boolean field it can accept any
+ # direct conditional expression usage.
+ if self.connection.features.has_native_boolean_field:
+ return True
+ # Most backends support direct EXISTS and lookups usage.
+ if isinstance(expression, (Exists, Lookup, WhereNode)):
+ return True
+ # Nested expression wrappers should be unwrapped.
+ if isinstance(expression, ExpressionWrapper) and expression.conditional:
+ return self.conditional_expression_supported_in_where_clause(
+ expression.expression
+ )
+ # Trust that direct usage of RawSQL can be used by itself.
+ if isinstance(expression, RawSQL) and expression.conditional:
+ return True
+ return False
def combine_expression(self, connector, sub_expressions):
"""
diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py
index 61fc9da3f4..7dee707820 100644
--- a/django/db/backends/mysql/operations.py
+++ b/django/db/backends/mysql/operations.py
@@ -3,7 +3,6 @@ import uuid
from django.conf import settings
from django.db.backends.base.operations import BaseDatabaseOperations
from django.db.backends.utils import split_tzname_delta
-from django.db.models import Exists, ExpressionWrapper, Lookup
from django.db.models.constants import OnConflict
from django.utils import timezone
from django.utils.encoding import force_str
@@ -393,19 +392,6 @@ class DatabaseOperations(BaseDatabaseOperations):
lookup = "JSON_UNQUOTE(%s)"
return lookup
- def conditional_expression_supported_in_where_clause(self, expression):
- # MySQL ignores indexes with boolean fields unless they're compared
- # directly to a boolean value.
- if isinstance(expression, (Exists, Lookup)):
- return True
- if isinstance(expression, ExpressionWrapper) and expression.conditional:
- return self.conditional_expression_supported_in_where_clause(
- expression.expression
- )
- if getattr(expression, "conditional", False):
- return False
- return super().conditional_expression_supported_in_where_clause(expression)
-
def on_conflict_suffix_sql(self, fields, on_conflict, update_fields, unique_fields):
if on_conflict == OnConflict.UPDATE:
conflict_suffix_sql = "ON DUPLICATE KEY UPDATE %(fields)s"
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index 802f27a3c6..d946e37fac 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -6,14 +6,7 @@ from django.conf import settings
from django.db import NotSupportedError
from django.db.backends.base.operations import BaseDatabaseOperations
from django.db.backends.utils import split_tzname_delta, strip_quotes, truncate_name
-from django.db.models import (
- AutoField,
- Exists,
- ExpressionWrapper,
- Lookup,
-)
-from django.db.models.expressions import RawSQL
-from django.db.models.sql.where import WhereNode
+from django.db.models import AutoField
from django.utils import timezone
from django.utils.encoding import force_bytes, force_str
from django.utils.functional import cached_property
@@ -705,21 +698,6 @@ END;
)
return super().subtract_temporals(internal_type, lhs, rhs)
- def conditional_expression_supported_in_where_clause(self, expression):
- """
- Oracle supports only EXISTS(...) or filters in the WHERE clause, others
- must be compared with True.
- """
- if isinstance(expression, (Exists, Lookup, WhereNode)):
- return True
- if isinstance(expression, ExpressionWrapper) and expression.conditional:
- return self.conditional_expression_supported_in_where_clause(
- expression.expression
- )
- if isinstance(expression, RawSQL) and expression.conditional:
- return True
- return False
-
def format_json_path_numeric_index(self, num):
if num < 0:
return "[last-%s]" % abs(num + 1) # Indexing is zero-based.
diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py
index d3fae82a10..b4a2575475 100644
--- a/django/db/backends/postgresql/features.py
+++ b/django/db/backends/postgresql/features.py
@@ -13,6 +13,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
can_return_rows_from_bulk_insert = True
can_return_rows_from_update = True
has_real_datatype = True
+ has_native_boolean_field = True
has_native_uuid_field = True
has_native_duration_field = True
has_native_json_field = True
diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py
index 4c08999fb6..eef7bc93a5 100644
--- a/django/db/models/lookups.py
+++ b/django/db/models/lookups.py
@@ -151,11 +151,16 @@ class Lookup(Expression):
# expression unless they're wrapped in a CASE WHEN.
wrapped = False
exprs = []
- 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)
+ if getattr(self.lhs, "conditional", False) and getattr(
+ self.rhs, "conditional", False
+ ):
+ 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)
lookup = type(self)(*exprs) if wrapped else self
return lookup.as_sql(compiler, connection)