diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py index 0b315aac02..03c97e6691 100644 --- a/django/db/backends/base/features.py +++ b/django/db/backends/base/features.py @@ -4,6 +4,8 @@ from django.utils.functional import cached_property class BaseDatabaseFeatures: gis_enabled = False + # Oracle can't group by LOB (large object) data types. + allows_group_by_lob = True allows_group_by_pk = False allows_group_by_selected_pks = False empty_fetchmany_value = [] diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index b0eebe5008..63d825f68a 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -4,6 +4,9 @@ from django.utils.functional import cached_property class DatabaseFeatures(BaseDatabaseFeatures): + # Oracle crashes with "ORA-00932: inconsistent datatypes: expected - got + # BLOB" when grouping by LOBs (#24096). + allows_group_by_lob = False interprets_empty_strings_as_nulls = True has_select_for_update = True has_select_for_update_nowait = True diff --git a/tests/expressions_case/tests.py b/tests/expressions_case/tests.py index 54c53ef4de..ec811ca511 100644 --- a/tests/expressions_case/tests.py +++ b/tests/expressions_case/tests.py @@ -5,6 +5,7 @@ from operator import attrgetter, itemgetter from uuid import UUID from django.core.exceptions import FieldError +from django.db import connection from django.db.models import ( BinaryField, BooleanField, Case, Count, DecimalField, F, GenericIPAddressField, IntegerField, Max, Min, Q, Sum, TextField, Value, @@ -56,10 +57,13 @@ class CaseExpressionTests(TestCase): O2OCaseTestModel.objects.create(o2o=o, integer=1) FKCaseTestModel.objects.create(fk=o, integer=5) - # GROUP BY on Oracle fails with TextField/BinaryField; see #24096. - cls.non_lob_fields = [ + cls.group_by_fields = [ f.name for f in CaseTestModel._meta.get_fields() - if not (f.is_relation and f.auto_created) and not isinstance(f, (BinaryField, TextField)) + if not (f.is_relation and f.auto_created) and + ( + connection.features.allows_group_by_lob or + not isinstance(f, (BinaryField, TextField)) + ) ] def test_annotate(self): @@ -197,7 +201,7 @@ class CaseExpressionTests(TestCase): def test_annotate_with_aggregation_in_value(self): self.assertQuerysetEqual( - CaseTestModel.objects.values(*self.non_lob_fields).annotate( + CaseTestModel.objects.values(*self.group_by_fields).annotate( min=Min('fk_rel__integer'), max=Max('fk_rel__integer'), ).annotate( @@ -212,7 +216,7 @@ class CaseExpressionTests(TestCase): def test_annotate_with_aggregation_in_condition(self): self.assertQuerysetEqual( - CaseTestModel.objects.values(*self.non_lob_fields).annotate( + CaseTestModel.objects.values(*self.group_by_fields).annotate( min=Min('fk_rel__integer'), max=Max('fk_rel__integer'), ).annotate( @@ -227,7 +231,7 @@ class CaseExpressionTests(TestCase): def test_annotate_with_aggregation_in_predicate(self): self.assertQuerysetEqual( - CaseTestModel.objects.values(*self.non_lob_fields).annotate( + CaseTestModel.objects.values(*self.group_by_fields).annotate( max=Max('fk_rel__integer'), ).annotate( test=Case( @@ -483,7 +487,7 @@ class CaseExpressionTests(TestCase): def test_filter_with_aggregation_in_value(self): self.assertQuerysetEqual( - CaseTestModel.objects.values(*self.non_lob_fields).annotate( + CaseTestModel.objects.values(*self.group_by_fields).annotate( min=Min('fk_rel__integer'), max=Max('fk_rel__integer'), ).filter( @@ -498,7 +502,7 @@ class CaseExpressionTests(TestCase): def test_filter_with_aggregation_in_condition(self): self.assertQuerysetEqual( - CaseTestModel.objects.values(*self.non_lob_fields).annotate( + CaseTestModel.objects.values(*self.group_by_fields).annotate( min=Min('fk_rel__integer'), max=Max('fk_rel__integer'), ).filter( @@ -513,7 +517,7 @@ class CaseExpressionTests(TestCase): def test_filter_with_aggregation_in_predicate(self): self.assertQuerysetEqual( - CaseTestModel.objects.values(*self.non_lob_fields).annotate( + CaseTestModel.objects.values(*self.group_by_fields).annotate( max=Max('fk_rel__integer'), ).filter( integer=Case( diff --git a/tests/gis_tests/geoapp/test_regress.py b/tests/gis_tests/geoapp/test_regress.py index 661124dcba..674f19ba54 100644 --- a/tests/gis_tests/geoapp/test_regress.py +++ b/tests/gis_tests/geoapp/test_regress.py @@ -5,7 +5,6 @@ from django.contrib.gis.shortcuts import render_to_kmz from django.db.models import Count, Min from django.test import TestCase, skipUnlessDBFeature -from ..utils import no_oracle from .models import City, PennsylvaniaCity, State, Truth @@ -65,10 +64,7 @@ class GeoRegressionTests(TestCase): # .count() should not throw TypeError in __eq__ self.assertEqual(cities_within_state.count(), 1) - # TODO: fix on Oracle -- get the following error because the SQL is ordered - # by a geometry object, which Oracle apparently doesn't like: - # ORA-22901: cannot compare nested table or VARRAY or LOB attributes of an object type - @no_oracle + @skipUnlessDBFeature('allows_group_by_lob') def test_defer_or_only_with_annotate(self): "Regression for #16409. Make sure defer() and only() work with annotate()" self.assertIsInstance(list(City.objects.annotate(Count('point')).defer('name')), list) diff --git a/tests/gis_tests/relatedapp/tests.py b/tests/gis_tests/relatedapp/tests.py index 5f003b78f2..2b4a0b0ed0 100644 --- a/tests/gis_tests/relatedapp/tests.py +++ b/tests/gis_tests/relatedapp/tests.py @@ -222,10 +222,7 @@ class RelatedGeoModelTest(TestCase): self.assertIn('Aurora', names) self.assertIn('Kecksburg', names) - # TODO: fix on Oracle -- get the following error because the SQL is ordered - # by a geometry object, which Oracle apparently doesn't like: - # ORA-22901: cannot compare nested table or VARRAY or LOB attributes of an object type - @no_oracle + @skipUnlessDBFeature('allows_group_by_lob') def test12a_count(self): "Testing `Count` aggregate on geo-fields." # The City, 'Fort Worth' uses the same location as Dallas. @@ -247,10 +244,7 @@ class RelatedGeoModelTest(TestCase): self.assertEqual(1, len(vqs)) self.assertEqual(3, vqs[0]['num_books']) - # TODO: fix on Oracle -- get the following error because the SQL is ordered - # by a geometry object, which Oracle apparently doesn't like: - # ORA-22901: cannot compare nested table or VARRAY or LOB attributes of an object type - @no_oracle + @skipUnlessDBFeature('allows_group_by_lob') def test13c_count(self): "Testing `Count` aggregate with `.values()`. See #15305." qs = Location.objects.filter(id=5).annotate(num_cities=Count('city')).values('id', 'point', 'num_cities') diff --git a/tests/model_fields/test_jsonfield.py b/tests/model_fields/test_jsonfield.py index f45589e075..1c63d70bf9 100644 --- a/tests/model_fields/test_jsonfield.py +++ b/tests/model_fields/test_jsonfield.py @@ -355,7 +355,7 @@ class TestQuerying(TestCase): operator.itemgetter('key', 'count'), ) - @skipIf(connection.vendor == 'oracle', "Oracle doesn't support grouping by LOBs, see #24096.") + @skipUnlessDBFeature('allows_group_by_lob') def test_ordering_grouping_by_count(self): qs = NullableJSONModel.objects.filter( value__isnull=False,