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:
parent
ae4077e13e
commit
64c3f049ea
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue