Fixed #31183 -- Added a feature flag for "<db> only supports UNBOUNDED together with PRECEDING and FOLLOWING".

This commit is contained in:
Tim Graham 2020-01-19 18:30:54 -05:00 committed by Mariusz Felisiak
parent 2a2ea4ee18
commit 227d0c7365
5 changed files with 19 additions and 18 deletions

View File

@ -246,6 +246,7 @@ class BaseDatabaseFeatures:
# Does the backend support window expressions (expression OVER (...))? # Does the backend support window expressions (expression OVER (...))?
supports_over_clause = False supports_over_clause = False
supports_frame_range_fixed_distance = False supports_frame_range_fixed_distance = False
only_supports_unbounded_with_preceding_and_following = False
# Does the backend support CAST with precision? # Does the backend support CAST with precision?
supports_cast_with_precision = True supports_cast_with_precision = True

View File

@ -658,7 +658,16 @@ class BaseDatabaseOperations:
return self.window_frame_start(start), self.window_frame_end(end) return self.window_frame_start(start), self.window_frame_end(end)
def window_frame_range_start_end(self, start=None, end=None): 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): def explain_query_prefix(self, format=None, **options):
if not self.connection.features.supports_explaining_query_execution: if not self.connection.features.supports_explaining_query_execution:

View File

@ -52,6 +52,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
$$ LANGUAGE plpgsql;""" $$ LANGUAGE plpgsql;"""
requires_casted_case_in_updates = True requires_casted_case_in_updates = True
supports_over_clause = True supports_over_clause = True
only_supports_unbounded_with_preceding_and_following = True
supports_aggregate_filter_clause = True supports_aggregate_filter_clause = True
supported_explain_formats = {'JSON', 'TEXT', 'XML', 'YAML'} supported_explain_formats = {'JSON', 'TEXT', 'XML', 'YAML'}
validates_explain_options = False # A query will error on invalid options. validates_explain_options = False # A query will error on invalid options.

View File

@ -1,7 +1,6 @@
from psycopg2.extras import Inet from psycopg2.extras import Inet
from django.conf import settings from django.conf import settings
from django.db import NotSupportedError
from django.db.backends.base.operations import BaseDatabaseOperations 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 "(interval '1 day' * (%s - %s))" % (lhs_sql, rhs_sql), params
return super().subtract_temporals(internal_type, lhs, rhs) 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): def explain_query_prefix(self, format=None, **options):
prefix = super().explain_query_prefix(format) prefix = super().explain_query_prefix(format)
extra = {} extra = {}

View File

@ -1,5 +1,5 @@
import datetime import datetime
from unittest import mock, skipIf, skipUnless from unittest import mock, skipIf
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.db import NotSupportedError, connection from django.db import NotSupportedError, connection
@ -750,9 +750,9 @@ class WindowFunctionTests(TestCase):
frame=RowRange(end='a'), frame=RowRange(end='a'),
))) )))
@skipUnless(connection.vendor == 'postgresql', 'Frame construction not allowed on PostgreSQL') @skipUnlessDBFeature('only_supports_unbounded_with_preceding_and_following')
def test_postgresql_illegal_range_frame_start(self): def test_unsupported_range_frame_start(self):
msg = 'PostgreSQL only supports UNBOUNDED together with PRECEDING and FOLLOWING.' msg = '%s only supports UNBOUNDED together with PRECEDING and FOLLOWING.' % connection.display_name
with self.assertRaisesMessage(NotSupportedError, msg): with self.assertRaisesMessage(NotSupportedError, msg):
list(Employee.objects.annotate(test=Window( list(Employee.objects.annotate(test=Window(
expression=Sum('salary'), expression=Sum('salary'),
@ -760,9 +760,9 @@ class WindowFunctionTests(TestCase):
frame=ValueRange(start=-1), frame=ValueRange(start=-1),
))) )))
@skipUnless(connection.vendor == 'postgresql', 'Frame construction not allowed on PostgreSQL') @skipUnlessDBFeature('only_supports_unbounded_with_preceding_and_following')
def test_postgresql_illegal_range_frame_end(self): def test_unsupported_range_frame_end(self):
msg = 'PostgreSQL only supports UNBOUNDED together with PRECEDING and FOLLOWING.' msg = '%s only supports UNBOUNDED together with PRECEDING and FOLLOWING.' % connection.display_name
with self.assertRaisesMessage(NotSupportedError, msg): with self.assertRaisesMessage(NotSupportedError, msg):
list(Employee.objects.annotate(test=Window( list(Employee.objects.annotate(test=Window(
expression=Sum('salary'), expression=Sum('salary'),