summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/backends/base/features.py3
-rw-r--r--django/db/backends/mysql/features.py1
-rw-r--r--django/db/backends/sqlite3/features.py1
-rw-r--r--django/db/models/expressions.py29
4 files changed, 14 insertions, 20 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py
index 4058b10a5e..14092e793e 100644
--- a/django/db/backends/base/features.py
+++ b/django/db/backends/base/features.py
@@ -88,6 +88,9 @@ class BaseDatabaseFeatures:
# Does the backend order NULL values as largest or smallest?
nulls_order_largest = False
+ # Does the backend support NULLS FIRST and NULLS LAST in ORDER BY?
+ supports_order_by_nulls_modifier = True
+
# The database's limit on the number of query parameters.
max_query_params = None
diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py
index 6a7f87ba27..9aaae2b5da 100644
--- a/django/db/backends/mysql/features.py
+++ b/django/db/backends/mysql/features.py
@@ -50,6 +50,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
db_functions_convert_bytes_to_str = True
# Neither MySQL nor MariaDB support partial indexes.
supports_partial_indexes = False
+ supports_order_by_nulls_modifier = False
@cached_property
def _mysql_storage_engine(self):
diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py
index c82be5c30d..6aebbc3262 100644
--- a/django/db/backends/sqlite3/features.py
+++ b/django/db/backends/sqlite3/features.py
@@ -44,3 +44,4 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_over_clause = Database.sqlite_version_info >= (3, 25, 0)
supports_frame_range_fixed_distance = Database.sqlite_version_info >= (3, 28, 0)
supports_aggregate_filter_clause = Database.sqlite_version_info >= (3, 30, 1)
+ supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 44a52c1dc9..13e0ef172c 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -1128,11 +1128,17 @@ class OrderBy(BaseExpression):
return [self.expression]
def as_sql(self, compiler, connection, template=None, **extra_context):
- if not template:
+ template = template or self.template
+ if connection.features.supports_order_by_nulls_modifier:
+ if self.nulls_last:
+ template = '%s NULLS LAST' % template
+ elif self.nulls_first:
+ template = '%s NULLS FIRST' % template
+ else:
if self.nulls_last:
- template = '%s NULLS LAST' % self.template
+ template = '%%(expression)s IS NULL, %s' % template
elif self.nulls_first:
- template = '%s NULLS FIRST' % self.template
+ template = '%%(expression)s IS NOT NULL, %s' % template
connection.ops.check_expression_support(self)
expression_sql, params = compiler.compile(self.expression)
placeholders = {
@@ -1144,23 +1150,6 @@ class OrderBy(BaseExpression):
params *= template.count('%(expression)s')
return (template % placeholders).rstrip(), params
- def as_sqlite(self, compiler, connection):
- template = None
- if connection.Database.sqlite_version_info < (3, 30, 0):
- if self.nulls_last:
- template = '%(expression)s IS NULL, %(expression)s %(ordering)s'
- elif self.nulls_first:
- template = '%(expression)s IS NOT NULL, %(expression)s %(ordering)s'
- return self.as_sql(compiler, connection, template=template)
-
- def as_mysql(self, compiler, connection):
- template = None
- if self.nulls_last:
- template = 'ISNULL(%(expression)s), %(expression)s %(ordering)s '
- elif self.nulls_first:
- template = 'IF(ISNULL(%(expression)s),0,1), %(expression)s %(ordering)s '
- return self.as_sql(compiler, connection, template=template)
-
def as_oracle(self, compiler, connection):
# Oracle doesn't allow ORDER BY EXISTS() unless it's wrapped in
# a CASE WHEN.