mirror of
https://github.com/django/django.git
synced 2025-01-03 06:55:47 +00:00
Fixed #35074 -- Fixed adding/removing indexes when spatial_index is changed on MySQL, PostgreSQL, and Oracle.
Co-authored-by: Mário Falcão <mario@falcao.dev>
This commit is contained in:
parent
ae1ee24178
commit
f8cc9285e1
@ -58,6 +58,43 @@ class MySQLGISSchemaEditor(DatabaseSchemaEditor):
|
|||||||
|
|
||||||
super().remove_field(model, field)
|
super().remove_field(model, field)
|
||||||
|
|
||||||
|
def _alter_field(
|
||||||
|
self,
|
||||||
|
model,
|
||||||
|
old_field,
|
||||||
|
new_field,
|
||||||
|
old_type,
|
||||||
|
new_type,
|
||||||
|
old_db_params,
|
||||||
|
new_db_params,
|
||||||
|
strict=False,
|
||||||
|
):
|
||||||
|
super()._alter_field(
|
||||||
|
model,
|
||||||
|
old_field,
|
||||||
|
new_field,
|
||||||
|
old_type,
|
||||||
|
new_type,
|
||||||
|
old_db_params,
|
||||||
|
new_db_params,
|
||||||
|
strict=strict,
|
||||||
|
)
|
||||||
|
|
||||||
|
old_field_spatial_index = (
|
||||||
|
isinstance(old_field, GeometryField)
|
||||||
|
and old_field.spatial_index
|
||||||
|
and not old_field.null
|
||||||
|
)
|
||||||
|
new_field_spatial_index = (
|
||||||
|
isinstance(new_field, GeometryField)
|
||||||
|
and new_field.spatial_index
|
||||||
|
and not new_field.null
|
||||||
|
)
|
||||||
|
if not old_field_spatial_index and new_field_spatial_index:
|
||||||
|
self.execute(self._create_spatial_index_sql(model, new_field))
|
||||||
|
elif old_field_spatial_index and not new_field_spatial_index:
|
||||||
|
self.execute(self._delete_spatial_index_sql(model, old_field))
|
||||||
|
|
||||||
def _create_spatial_index_name(self, model, field):
|
def _create_spatial_index_name(self, model, field):
|
||||||
return "%s_%s_id" % (model._meta.db_table, field.column)
|
return "%s_%s_id" % (model._meta.db_table, field.column)
|
||||||
|
|
||||||
|
@ -98,6 +98,39 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor):
|
|||||||
self.execute(sql)
|
self.execute(sql)
|
||||||
self.geometry_sql = []
|
self.geometry_sql = []
|
||||||
|
|
||||||
|
def _alter_field(
|
||||||
|
self,
|
||||||
|
model,
|
||||||
|
old_field,
|
||||||
|
new_field,
|
||||||
|
old_type,
|
||||||
|
new_type,
|
||||||
|
old_db_params,
|
||||||
|
new_db_params,
|
||||||
|
strict=False,
|
||||||
|
):
|
||||||
|
super()._alter_field(
|
||||||
|
model,
|
||||||
|
old_field,
|
||||||
|
new_field,
|
||||||
|
old_type,
|
||||||
|
new_type,
|
||||||
|
old_db_params,
|
||||||
|
new_db_params,
|
||||||
|
strict=strict,
|
||||||
|
)
|
||||||
|
|
||||||
|
old_field_spatial_index = (
|
||||||
|
isinstance(old_field, GeometryField) and old_field.spatial_index
|
||||||
|
)
|
||||||
|
new_field_spatial_index = (
|
||||||
|
isinstance(new_field, GeometryField) and new_field.spatial_index
|
||||||
|
)
|
||||||
|
if not old_field_spatial_index and new_field_spatial_index:
|
||||||
|
self.execute(self._create_spatial_index_sql(model, new_field))
|
||||||
|
elif old_field_spatial_index and not new_field_spatial_index:
|
||||||
|
self.execute(self._delete_spatial_index_sql(model, old_field))
|
||||||
|
|
||||||
def _create_spatial_index_name(self, model, field):
|
def _create_spatial_index_name(self, model, field):
|
||||||
# Oracle doesn't allow object names > 30 characters. Use this scheme
|
# Oracle doesn't allow object names > 30 characters. Use this scheme
|
||||||
# instead of self._create_index_name() for backwards compatibility.
|
# instead of self._create_index_name() for backwards compatibility.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from django.contrib.gis.db.models import GeometryField
|
||||||
from django.db.backends.postgresql.schema import DatabaseSchemaEditor
|
from django.db.backends.postgresql.schema import DatabaseSchemaEditor
|
||||||
from django.db.models.expressions import Col, Func
|
from django.db.models.expressions import Col, Func
|
||||||
|
|
||||||
@ -58,6 +59,39 @@ class PostGISSchemaEditor(DatabaseSchemaEditor):
|
|||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _alter_field(
|
||||||
|
self,
|
||||||
|
model,
|
||||||
|
old_field,
|
||||||
|
new_field,
|
||||||
|
old_type,
|
||||||
|
new_type,
|
||||||
|
old_db_params,
|
||||||
|
new_db_params,
|
||||||
|
strict=False,
|
||||||
|
):
|
||||||
|
super()._alter_field(
|
||||||
|
model,
|
||||||
|
old_field,
|
||||||
|
new_field,
|
||||||
|
old_type,
|
||||||
|
new_type,
|
||||||
|
old_db_params,
|
||||||
|
new_db_params,
|
||||||
|
strict=strict,
|
||||||
|
)
|
||||||
|
|
||||||
|
old_field_spatial_index = (
|
||||||
|
isinstance(old_field, GeometryField) and old_field.spatial_index
|
||||||
|
)
|
||||||
|
new_field_spatial_index = (
|
||||||
|
isinstance(new_field, GeometryField) and new_field.spatial_index
|
||||||
|
)
|
||||||
|
if not old_field_spatial_index and new_field_spatial_index:
|
||||||
|
self.execute(self._create_spatial_index_sql(model, new_field))
|
||||||
|
elif old_field_spatial_index and not new_field_spatial_index:
|
||||||
|
self.execute(self._delete_spatial_index_sql(model, old_field))
|
||||||
|
|
||||||
def _create_spatial_index_name(self, model, field):
|
def _create_spatial_index_name(self, model, field):
|
||||||
return self._create_index_name(model._meta.db_table, [field.column], "_id")
|
return self._create_index_name(model._meta.db_table, [field.column], "_id")
|
||||||
|
|
||||||
@ -84,3 +118,7 @@ class PostGISSchemaEditor(DatabaseSchemaEditor):
|
|||||||
opclasses=opclasses,
|
opclasses=opclasses,
|
||||||
expressions=expressions,
|
expressions=expressions,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _delete_spatial_index_sql(self, model, field):
|
||||||
|
index_name = self._create_spatial_index_name(model, field)
|
||||||
|
return self._delete_index_sql(model, index_name)
|
||||||
|
@ -92,6 +92,20 @@ class OperationTestCase(TransactionTestCase):
|
|||||||
else:
|
else:
|
||||||
self.assertIn([column], [c["columns"] for c in constraints.values()])
|
self.assertIn([column], [c["columns"] for c in constraints.values()])
|
||||||
|
|
||||||
|
def assertSpatialIndexNotExists(self, table, column, raster=False):
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
constraints = connection.introspection.get_constraints(cursor, table)
|
||||||
|
if raster:
|
||||||
|
self.assertFalse(
|
||||||
|
any(
|
||||||
|
"st_convexhull(%s)" % column in c["definition"]
|
||||||
|
for c in constraints.values()
|
||||||
|
if c["definition"] is not None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.assertNotIn([column], [c["columns"] for c in constraints.values()])
|
||||||
|
|
||||||
def alter_gis_model(
|
def alter_gis_model(
|
||||||
self,
|
self,
|
||||||
migration_class,
|
migration_class,
|
||||||
@ -239,6 +253,102 @@ class OperationTests(OperationTestCase):
|
|||||||
if connection.features.supports_raster:
|
if connection.features.supports_raster:
|
||||||
self.assertSpatialIndexExists("gis_neighborhood", "rast", raster=True)
|
self.assertSpatialIndexExists("gis_neighborhood", "rast", raster=True)
|
||||||
|
|
||||||
|
@skipUnlessDBFeature("can_alter_geometry_field")
|
||||||
|
def test_alter_field_add_spatial_index(self):
|
||||||
|
if not self.has_spatial_indexes:
|
||||||
|
self.skipTest("No support for Spatial indexes")
|
||||||
|
|
||||||
|
self.alter_gis_model(
|
||||||
|
migrations.AddField,
|
||||||
|
"Neighborhood",
|
||||||
|
"point",
|
||||||
|
fields.PointField,
|
||||||
|
field_class_kwargs={"spatial_index": False},
|
||||||
|
)
|
||||||
|
self.assertSpatialIndexNotExists("gis_neighborhood", "point")
|
||||||
|
|
||||||
|
self.alter_gis_model(
|
||||||
|
migrations.AlterField,
|
||||||
|
"Neighborhood",
|
||||||
|
"point",
|
||||||
|
fields.PointField,
|
||||||
|
field_class_kwargs={"spatial_index": True},
|
||||||
|
)
|
||||||
|
self.assertSpatialIndexExists("gis_neighborhood", "point")
|
||||||
|
|
||||||
|
@skipUnlessDBFeature("can_alter_geometry_field")
|
||||||
|
def test_alter_field_remove_spatial_index(self):
|
||||||
|
if not self.has_spatial_indexes:
|
||||||
|
self.skipTest("No support for Spatial indexes")
|
||||||
|
|
||||||
|
self.assertSpatialIndexExists("gis_neighborhood", "geom")
|
||||||
|
|
||||||
|
self.alter_gis_model(
|
||||||
|
migrations.AlterField,
|
||||||
|
"Neighborhood",
|
||||||
|
"geom",
|
||||||
|
fields.MultiPolygonField,
|
||||||
|
field_class_kwargs={"spatial_index": False},
|
||||||
|
)
|
||||||
|
self.assertSpatialIndexNotExists("gis_neighborhood", "geom")
|
||||||
|
|
||||||
|
@skipUnlessDBFeature("can_alter_geometry_field")
|
||||||
|
@skipUnless(connection.vendor == "mysql", "MySQL specific test")
|
||||||
|
def test_alter_field_nullable_with_spatial_index(self):
|
||||||
|
if not self.has_spatial_indexes:
|
||||||
|
self.skipTest("No support for Spatial indexes")
|
||||||
|
|
||||||
|
self.alter_gis_model(
|
||||||
|
migrations.AddField,
|
||||||
|
"Neighborhood",
|
||||||
|
"point",
|
||||||
|
fields.PointField,
|
||||||
|
field_class_kwargs={"spatial_index": False, "null": True},
|
||||||
|
)
|
||||||
|
# MySQL doesn't support spatial indexes on NULL columns.
|
||||||
|
self.assertSpatialIndexNotExists("gis_neighborhood", "point")
|
||||||
|
|
||||||
|
self.alter_gis_model(
|
||||||
|
migrations.AlterField,
|
||||||
|
"Neighborhood",
|
||||||
|
"point",
|
||||||
|
fields.PointField,
|
||||||
|
field_class_kwargs={"spatial_index": True, "null": True},
|
||||||
|
)
|
||||||
|
self.assertSpatialIndexNotExists("gis_neighborhood", "point")
|
||||||
|
|
||||||
|
self.alter_gis_model(
|
||||||
|
migrations.AlterField,
|
||||||
|
"Neighborhood",
|
||||||
|
"point",
|
||||||
|
fields.PointField,
|
||||||
|
field_class_kwargs={"spatial_index": False, "null": True},
|
||||||
|
)
|
||||||
|
self.assertSpatialIndexNotExists("gis_neighborhood", "point")
|
||||||
|
|
||||||
|
@skipUnlessDBFeature("can_alter_geometry_field")
|
||||||
|
def test_alter_field_with_spatial_index(self):
|
||||||
|
if not self.has_spatial_indexes:
|
||||||
|
self.skipTest("No support for Spatial indexes")
|
||||||
|
|
||||||
|
self.alter_gis_model(
|
||||||
|
migrations.AddField,
|
||||||
|
"Neighborhood",
|
||||||
|
"point",
|
||||||
|
fields.PointField,
|
||||||
|
field_class_kwargs={"spatial_index": True},
|
||||||
|
)
|
||||||
|
self.assertSpatialIndexExists("gis_neighborhood", "point")
|
||||||
|
|
||||||
|
self.alter_gis_model(
|
||||||
|
migrations.AlterField,
|
||||||
|
"Neighborhood",
|
||||||
|
"point",
|
||||||
|
fields.PointField,
|
||||||
|
field_class_kwargs={"spatial_index": True, "srid": 3086},
|
||||||
|
)
|
||||||
|
self.assertSpatialIndexExists("gis_neighborhood", "point")
|
||||||
|
|
||||||
@skipUnlessDBFeature("supports_3d_storage")
|
@skipUnlessDBFeature("supports_3d_storage")
|
||||||
def test_add_3d_field_opclass(self):
|
def test_add_3d_field_opclass(self):
|
||||||
if not connection.ops.postgis:
|
if not connection.ops.postgis:
|
||||||
|
Loading…
Reference in New Issue
Block a user