summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2020-01-19 18:30:54 -0500
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-01-20 20:03:35 +0100
commit227d0c7365cfd0a64d021cb9bdcf77bed2d3f170 (patch)
tree3d166c4ca5e98352857cf08f92ec2c1c84bb73ec
parent2a2ea4ee18fdcf2c95bf6435bc63b74623e3085b (diff)
Fixed #31183 -- Added a feature flag for "<db> only supports UNBOUNDED together with PRECEDING and FOLLOWING".
-rw-r--r--django/db/backends/base/features.py1
-rw-r--r--django/db/backends/base/operations.py11
-rw-r--r--django/db/backends/postgresql/features.py1
-rw-r--r--django/db/backends/postgresql/operations.py10
-rw-r--r--tests/expressions_window/tests.py14
5 files changed, 19 insertions, 18 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py
index 14092e793e..45c62f463b 100644
--- a/django/db/backends/base/features.py
+++ b/django/db/backends/base/features.py
@@ -246,6 +246,7 @@ class BaseDatabaseFeatures:
# Does the backend support window expressions (expression OVER (...))?
supports_over_clause = False
supports_frame_range_fixed_distance = False
+ only_supports_unbounded_with_preceding_and_following = False
# Does the backend support CAST with precision?
supports_cast_with_precision = True
diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py
index 43220a18bc..6e1187fd37 100644
--- a/django/db/backends/base/operations.py
+++ b/django/db/backends/base/operations.py
@@ -658,7 +658,16 @@ class BaseDatabaseOperations:
return self.window_frame_start(start), self.window_frame_end(end)
def window_frame_range_start_end(self, start=None, end=None):
- return self.window_frame_rows_start_end(start, end)
+ start_, end_ = self.window_frame_rows_start_end(start, end)
+ if (
+ self.connection.features.only_supports_unbounded_with_preceding_and_following and
+ ((start and start < 0) or (end and end > 0))
+ ):
+ raise NotSupportedError(
+ '%s only supports UNBOUNDED together with PRECEDING and '
+ 'FOLLOWING.' % self.connection.display_name
+ )
+ return start_, end_
def explain_query_prefix(self, format=None, **options):
if not self.connection.features.supports_explaining_query_execution:
diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py
index fad87081b0..67c04d89dc 100644
--- a/django/db/backends/postgresql/features.py
+++ b/django/db/backends/postgresql/features.py
@@ -52,6 +52,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
$$ LANGUAGE plpgsql;"""
requires_casted_case_in_updates = True
supports_over_clause = True
+ only_supports_unbounded_with_preceding_and_following = True
supports_aggregate_filter_clause = True
supported_explain_formats = {'JSON', 'TEXT', 'XML', 'YAML'}
validates_explain_options = False # A query will error on invalid options.
diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py
index ee7787c560..e8355a0e58 100644
--- a/django/db/backends/postgresql/operations.py
+++ b/django/db/backends/postgresql/operations.py
@@ -1,7 +1,6 @@
from psycopg2.extras import Inet
from django.conf import settings
-from django.db import NotSupportedError
from django.db.backends.base.operations import BaseDatabaseOperations
@@ -275,15 +274,6 @@ class DatabaseOperations(BaseDatabaseOperations):
return "(interval '1 day' * (%s - %s))" % (lhs_sql, rhs_sql), params
return super().subtract_temporals(internal_type, lhs, rhs)
- def window_frame_range_start_end(self, start=None, end=None):
- start_, end_ = super().window_frame_range_start_end(start, end)
- if (start and start < 0) or (end and end > 0):
- raise NotSupportedError(
- 'PostgreSQL only supports UNBOUNDED together with PRECEDING '
- 'and FOLLOWING.'
- )
- return start_, end_
-
def explain_query_prefix(self, format=None, **options):
prefix = super().explain_query_prefix(format)
extra = {}
diff --git a/tests/expressions_window/tests.py b/tests/expressions_window/tests.py
index 4df58bf27d..37b0e95247 100644
--- a/tests/expressions_window/tests.py
+++ b/tests/expressions_window/tests.py
@@ -1,5 +1,5 @@
import datetime
-from unittest import mock, skipIf, skipUnless
+from unittest import mock, skipIf
from django.core.exceptions import FieldError
from django.db import NotSupportedError, connection
@@ -750,9 +750,9 @@ class WindowFunctionTests(TestCase):
frame=RowRange(end='a'),
)))
- @skipUnless(connection.vendor == 'postgresql', 'Frame construction not allowed on PostgreSQL')
- def test_postgresql_illegal_range_frame_start(self):
- msg = 'PostgreSQL only supports UNBOUNDED together with PRECEDING and FOLLOWING.'
+ @skipUnlessDBFeature('only_supports_unbounded_with_preceding_and_following')
+ def test_unsupported_range_frame_start(self):
+ msg = '%s only supports UNBOUNDED together with PRECEDING and FOLLOWING.' % connection.display_name
with self.assertRaisesMessage(NotSupportedError, msg):
list(Employee.objects.annotate(test=Window(
expression=Sum('salary'),
@@ -760,9 +760,9 @@ class WindowFunctionTests(TestCase):
frame=ValueRange(start=-1),
)))
- @skipUnless(connection.vendor == 'postgresql', 'Frame construction not allowed on PostgreSQL')
- def test_postgresql_illegal_range_frame_end(self):
- msg = 'PostgreSQL only supports UNBOUNDED together with PRECEDING and FOLLOWING.'
+ @skipUnlessDBFeature('only_supports_unbounded_with_preceding_and_following')
+ def test_unsupported_range_frame_end(self):
+ msg = '%s only supports UNBOUNDED together with PRECEDING and FOLLOWING.' % connection.display_name
with self.assertRaisesMessage(NotSupportedError, msg):
list(Employee.objects.annotate(test=Window(
expression=Sum('salary'),