Fixed #33047 -- Fixed CheckConstraint crash with GIS lookups on PostGIS and MySQL GIS backends.

Thanks Daniel Swain for the report and Arsalan Ghassemi for the initial
patch.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
Claude Paroz 2021-11-27 11:30:58 +01:00 committed by Mariusz Felisiak
parent ae4077e13e
commit 64c3f049ea
5 changed files with 43 additions and 4 deletions

View File

@ -22,6 +22,11 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor):
return True return True
return super().skip_default(field) return super().skip_default(field)
def quote_value(self, value):
if isinstance(value, self.connection.ops.Adapter):
return super().quote_value(str(value))
return super().quote_value(value)
def column_sql(self, model, field, include_default=False): def column_sql(self, model, field, include_default=False):
column_sql = super().column_sql(model, field, include_default) column_sql = super().column_sql(model, field, include_default)
# MySQL doesn't support spatial indexes on NULL columns # MySQL doesn't support spatial indexes on NULL columns

View File

@ -2,6 +2,7 @@ from django.contrib.gis.db.backends.base.features import BaseSpatialFeatures
from django.db.backends.oracle.features import ( from django.db.backends.oracle.features import (
DatabaseFeatures as OracleDatabaseFeatures, DatabaseFeatures as OracleDatabaseFeatures,
) )
from django.utils.functional import cached_property
class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures): class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures):
@ -12,3 +13,13 @@ class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures):
supports_dwithin_distance_expr = False supports_dwithin_distance_expr = False
supports_tolerance_parameter = True supports_tolerance_parameter = True
unsupported_geojson_options = {'bbox', 'crs', 'precision'} unsupported_geojson_options = {'bbox', 'crs', 'precision'}
@cached_property
def django_test_skips(self):
skips = super().django_test_skips
skips.update({
"Oracle doesn't support spatial operators in constraints.": {
'gis_tests.gis_migrations.test_operations.OperationTests.test_add_check_constraint',
},
})
return skips

View File

@ -31,6 +31,11 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor):
def geo_quote_name(self, name): def geo_quote_name(self, name):
return self.connection.ops.geo_quote_name(name) return self.connection.ops.geo_quote_name(name)
def quote_value(self, value):
if isinstance(value, self.connection.ops.Adapter):
return super().quote_value(str(value))
return super().quote_value(value)
def column_sql(self, model, field, include_default=False): def column_sql(self, model, field, include_default=False):
column_sql = super().column_sql(model, field, include_default) column_sql = super().column_sql(model, field, include_default)
if isinstance(field, GeometryField): if isinstance(field, GeometryField):

View File

@ -60,10 +60,10 @@ class PostGISAdapter:
""" """
if self.is_geometry: if self.is_geometry:
# Psycopg will figure out whether to use E'\\000' or '\000'. # Psycopg will figure out whether to use E'\\000' or '\000'.
return '%s(%s)' % ( return b'%s(%s)' % (
'ST_GeogFromWKB' if self.geography else 'ST_GeomFromEWKB', b'ST_GeogFromWKB' if self.geography else b'ST_GeomFromEWKB',
self._adapter.getquoted().decode() self._adapter.getquoted()
) )
else: else:
# For rasters, add explicit type cast to WKB string. # For rasters, add explicit type cast to WKB string.
return "'%s'::raster" % self.ewkb return b"'%s'::raster" % self.ewkb.encode()

View File

@ -235,6 +235,24 @@ class OperationTests(OperationTestCase):
) )
self.assertFalse(Neighborhood.objects.first().geom.hasz) self.assertFalse(Neighborhood.objects.first().geom.hasz)
@skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints')
def test_add_check_constraint(self):
Neighborhood = self.current_state.apps.get_model('gis', 'Neighborhood')
poly = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
constraint = models.CheckConstraint(
check=models.Q(geom=poly),
name='geom_within_constraint',
)
Neighborhood._meta.constraints = [constraint]
with connection.schema_editor() as editor:
editor.add_constraint(Neighborhood, constraint)
with connection.cursor() as cursor:
constraints = connection.introspection.get_constraints(
cursor,
Neighborhood._meta.db_table,
)
self.assertIn('geom_within_constraint', constraints)
@skipIfDBFeature('supports_raster') @skipIfDBFeature('supports_raster')
class NoRasterSupportTests(OperationTestCase): class NoRasterSupportTests(OperationTestCase):