diff --git a/django/contrib/gis/db/backends/base/features.py b/django/contrib/gis/db/backends/base/features.py index 73240f6ccb..cf52c4e122 100644 --- a/django/contrib/gis/db/backends/base/features.py +++ b/django/contrib/gis/db/backends/base/features.py @@ -54,6 +54,10 @@ class BaseSpatialFeatures: # Set of options that AsGeoJSON() doesn't support. unsupported_geojson_options = {} + # Does Intersection() return None (rather than an empty GeometryCollection) + # for empty results? + empty_intersection_returns_none = True + @property def supports_bbcontains_lookup(self): return 'bbcontains' in self.connection.ops.gis_operators diff --git a/django/contrib/gis/db/backends/mysql/features.py b/django/contrib/gis/db/backends/mysql/features.py index 72e59d2278..613ccce9c5 100644 --- a/django/contrib/gis/db/backends/mysql/features.py +++ b/django/contrib/gis/db/backends/mysql/features.py @@ -17,8 +17,11 @@ class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures): unsupported_geojson_options = {'crs'} @cached_property - def supports_empty_geometry_collection(self): - return self.connection.mysql_version >= (5, 7, 5) + def empty_intersection_returns_none(self): + return ( + not self.connection.mysql_is_mariadb and + self.connection.mysql_version < (5, 7, 5) + ) @cached_property def supports_geometry_field_unique_index(self): diff --git a/django/contrib/gis/db/backends/postgis/features.py b/django/contrib/gis/db/backends/postgis/features.py index f73e5547a5..29a1079631 100644 --- a/django/contrib/gis/db/backends/postgis/features.py +++ b/django/contrib/gis/db/backends/postgis/features.py @@ -10,3 +10,4 @@ class DatabaseFeatures(BaseSpatialFeatures, Psycopg2DatabaseFeatures): supports_3d_functions = True supports_raster = True supports_empty_geometries = True + empty_intersection_returns_none = False diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py index 0c76aa9730..1a1eea8f4d 100644 --- a/tests/gis_tests/geoapp/test_functions.py +++ b/tests/gis_tests/geoapp/test_functions.py @@ -12,7 +12,7 @@ from django.db import NotSupportedError, connection from django.db.models import IntegerField, Sum, Value from django.test import TestCase, skipUnlessDBFeature -from ..utils import FuncTestMixin, mariadb, mysql, oracle, postgis, spatialite +from ..utils import FuncTestMixin, mariadb, mysql, oracle, postgis from .models import City, Country, CountryWebMercator, State, Track @@ -295,11 +295,10 @@ class GISFunctionsTests(FuncTestMixin, TestCase): geom = Point(5, 23, srid=4326) qs = Country.objects.annotate(inter=functions.Intersection('mpoly', geom)) for c in qs: - if spatialite or (mysql and not connection.features.supports_empty_geometry_collection) or oracle: - # When the intersection is empty, some databases return None. - expected = None - else: - expected = c.mpoly.intersection(geom) + expected = ( + None if connection.features.empty_intersection_returns_none + else c.mpoly.intersection(geom) + ) self.assertEqual(c.inter, expected) @skipUnlessDBFeature("has_IsValid_function") @@ -570,8 +569,11 @@ class GISFunctionsTests(FuncTestMixin, TestCase): return for c in qs: self.assertTrue(c.mpoly.difference(geom).equals(c.difference)) - if not (spatialite or mysql): - self.assertEqual(c.mpoly.intersection(geom), c.intersection) + expected_intersection = ( + None if connection.features.empty_intersection_returns_none + else c.mpoly.intersection(geom) + ) + self.assertEqual(c.intersection, expected_intersection) self.assertTrue(c.mpoly.sym_difference(geom).equals(c.sym_difference)) self.assertTrue(c.mpoly.union(geom).equals(c.union))