diff --git a/mssql/features.py b/mssql/features.py index d1562bdd..e688012a 100644 --- a/mssql/features.py +++ b/mssql/features.py @@ -33,6 +33,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_index_on_text_field = False supports_json_field_contains = False supports_order_by_nulls_modifier = False + supports_over_clause = True supports_paramstyle_pyformat = False supports_primitives_in_json_field = False supports_regex_backreferencing = True diff --git a/mssql/functions.py b/mssql/functions.py index d22eca2e..7c110006 100644 --- a/mssql/functions.py +++ b/mssql/functions.py @@ -4,10 +4,11 @@ import json from django import VERSION -from django.db.models import BooleanField -from django.db.models.functions import Cast +from django.db import NotSupportedError +from django.db.models import BooleanField, Value +from django.db.models.functions import Cast, NthValue from django.db.models.functions.math import ATan2, Log, Ln, Mod, Round -from django.db.models.expressions import Case, Exists, OrderBy, When +from django.db.models.expressions import Case, Exists, OrderBy, When, Window from django.db.models.lookups import Lookup, In from django.db.models import lookups @@ -51,10 +52,21 @@ def sqlserver_mod(self, compiler, connection): ) +def sqlserver_nth_value(self, compiler, connection, **extra_content): + raise NotSupportedError('This backend does not support the NthValue function') + + def sqlserver_round(self, compiler, connection, **extra_context): return self.as_sql(compiler, connection, template='%(function)s(%(expressions)s, 0)', **extra_context) +def sqlserver_window(self, compiler, connection, template=None): + # MSSQL window functions require an OVER clause with ORDER BY + if self.order_by is None: + self.order_by = Value('SELECT NULL') + return self.as_sql(compiler, connection, template) + + def sqlserver_exists(self, compiler, connection, template=None, **extra_context): # MS SQL doesn't allow EXISTS() in the SELECT list, so wrap it with a # CASE WHEN expression. Change the template since the When expression @@ -179,7 +191,9 @@ def json_HasKeyLookup(self, compiler, connection): Ln.as_microsoft = sqlserver_ln Log.as_microsoft = sqlserver_log Mod.as_microsoft = sqlserver_mod +NthValue.as_microsoft = sqlserver_nth_value Round.as_microsoft = sqlserver_round +Window.as_microsoft = sqlserver_window if DJANGO3: Lookup.as_microsoft = sqlserver_lookup @@ -187,3 +201,4 @@ def json_HasKeyLookup(self, compiler, connection): Exists.as_microsoft = sqlserver_exists OrderBy.as_microsoft = sqlserver_orderby + diff --git a/testapp/settings.py b/testapp/settings.py index 626fa2c3..7af5bf96 100644 --- a/testapp/settings.py +++ b/testapp/settings.py @@ -54,6 +54,9 @@ 'expressions.tests.FTimeDeltaTests.test_duration_with_datetime_microseconds', 'expressions.tests.IterableLookupInnerExpressionsTests.test_expressions_in_lookups_join_choice', 'expressions_case.tests.CaseExpressionTests.test_annotate_with_in_clause', + 'expressions_window.tests.WindowFunctionTests.test_nth_returns_null', + 'expressions_window.tests.WindowFunctionTests.test_nthvalue', + 'expressions_window.tests.WindowFunctionTests.test_range_n_preceding_and_following', 'ordering.tests.OrderingTests.test_orders_nulls_first_on_filtered_subquery', 'queries.test_bulk_update.BulkUpdateNoteTests.test_set_field_to_null', 'get_or_create.tests.UpdateOrCreateTransactionTests.test_creation_in_transaction',