summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2014-09-18 07:42:51 -0400
committerTim Graham <timograham@gmail.com>2014-09-25 20:16:54 -0400
commita8f07530a74839d20f1f2fb2cc7f5b8ef2215782 (patch)
tree8788dbe2a550696354288f28c646149182c77b8a
parent45bd7b3bd9008941580c100b9fc7361e3ff3ff0d (diff)
Fixed #23537 -- Added Oracle GIS SchemaEditor.
Thanks Shai Berger for review.
-rw-r--r--django/contrib/gis/db/backends/oracle/base.py4
-rw-r--r--django/contrib/gis/db/backends/oracle/operations.py3
-rw-r--r--django/contrib/gis/db/backends/oracle/schema.py94
-rw-r--r--django/contrib/gis/tests/gis_migrations/test_operations.py21
-rw-r--r--docs/releases/1.7.1.txt3
5 files changed, 117 insertions, 8 deletions
diff --git a/django/contrib/gis/db/backends/oracle/base.py b/django/contrib/gis/db/backends/oracle/base.py
index c1b48bccfa..fad281a3f9 100644
--- a/django/contrib/gis/db/backends/oracle/base.py
+++ b/django/contrib/gis/db/backends/oracle/base.py
@@ -6,6 +6,7 @@ from django.contrib.gis.db.backends.base import BaseSpatialFeatures
from django.contrib.gis.db.backends.oracle.creation import OracleCreation
from django.contrib.gis.db.backends.oracle.introspection import OracleIntrospection
from django.contrib.gis.db.backends.oracle.operations import OracleOperations
+from django.contrib.gis.db.backends.oracle.schema import OracleGISSchemaEditor
class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures):
@@ -20,3 +21,6 @@ class DatabaseWrapper(OracleDatabaseWrapper):
self.ops = OracleOperations(self)
self.creation = OracleCreation(self)
self.introspection = OracleIntrospection(self)
+
+ def schema_editor(self, *args, **kwargs):
+ return OracleGISSchemaEditor(self, *args, **kwargs)
diff --git a/django/contrib/gis/db/backends/oracle/operations.py b/django/contrib/gis/db/backends/oracle/operations.py
index aa06e98433..1c58c335e3 100644
--- a/django/contrib/gis/db/backends/oracle/operations.py
+++ b/django/contrib/gis/db/backends/oracle/operations.py
@@ -142,6 +142,9 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
truncate_params = {'relate': None}
+ def geo_quote_name(self, name):
+ return super(OracleOperations, self).geo_quote_name(name).upper()
+
def get_db_converters(self, internal_type):
converters = super(OracleOperations, self).get_db_converters(internal_type)
geometry_fields = (
diff --git a/django/contrib/gis/db/backends/oracle/schema.py b/django/contrib/gis/db/backends/oracle/schema.py
new file mode 100644
index 0000000000..973a56e766
--- /dev/null
+++ b/django/contrib/gis/db/backends/oracle/schema.py
@@ -0,0 +1,94 @@
+from django.contrib.gis.db.models.fields import GeometryField
+from django.db.backends.oracle.schema import DatabaseSchemaEditor
+from django.db.backends.utils import truncate_name
+
+
+class OracleGISSchemaEditor(DatabaseSchemaEditor):
+ sql_add_geometry_metadata = ("""
+ INSERT INTO USER_SDO_GEOM_METADATA
+ ("TABLE_NAME", "COLUMN_NAME", "DIMINFO", "SRID")
+ VALUES (
+ %(table)s,
+ %(column)s,
+ MDSYS.SDO_DIM_ARRAY(
+ MDSYS.SDO_DIM_ELEMENT('LONG', %(dim0)s, %(dim2)s, %(tolerance)s),
+ MDSYS.SDO_DIM_ELEMENT('LAT', %(dim1)s, %(dim3)s, %(tolerance)s)
+ ),
+ %(srid)s
+ )""")
+ sql_add_spatial_index = 'CREATE INDEX %(index)s ON %(table)s(%(column)s) INDEXTYPE IS MDSYS.SPATIAL_INDEX'
+ sql_drop_spatial_index = 'DROP INDEX %(index)s'
+ sql_clear_geometry_table_metadata = 'DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = %(table)s'
+ sql_clear_geometry_field_metadata = (
+ 'DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = %(table)s '
+ 'AND COLUMN_NAME = %(column)s'
+ )
+
+ def __init__(self, *args, **kwargs):
+ super(OracleGISSchemaEditor, self).__init__(*args, **kwargs)
+ self.geometry_sql = []
+
+ def geo_quote_name(self, name):
+ return self.connection.ops.geo_quote_name(name)
+
+ def column_sql(self, model, field, include_default=False):
+ column_sql = super(OracleGISSchemaEditor, self).column_sql(model, field, include_default)
+ if isinstance(field, GeometryField):
+ db_table = model._meta.db_table
+ self.geometry_sql.append(
+ self.sql_add_geometry_metadata % {
+ 'table': self.geo_quote_name(db_table),
+ 'column': self.geo_quote_name(field.column),
+ 'dim0': field._extent[0],
+ 'dim1': field._extent[1],
+ 'dim2': field._extent[2],
+ 'dim3': field._extent[3],
+ 'tolerance': field._tolerance,
+ 'srid': field.srid,
+ }
+ )
+ if field.spatial_index:
+ self.geometry_sql.append(
+ self.sql_add_spatial_index % {
+ 'index': self.quote_name(self._create_spatial_index_name(model, field)),
+ 'table': self.quote_name(db_table),
+ 'column': self.quote_name(field.column),
+ }
+ )
+ return column_sql
+
+ def create_model(self, model):
+ super(OracleGISSchemaEditor, self).create_model(model)
+ self.run_geometry_sql()
+
+ def delete_model(self, model):
+ super(OracleGISSchemaEditor, self).delete_model(model)
+ self.execute(self.sql_clear_geometry_table_metadata % {
+ 'table': self.geo_quote_name(model._meta.db_table),
+ })
+
+ def add_field(self, model, field):
+ super(OracleGISSchemaEditor, self).add_field(model, field)
+ self.run_geometry_sql()
+
+ def remove_field(self, model, field):
+ if isinstance(field, GeometryField):
+ self.execute(self.sql_clear_geometry_field_metadata % {
+ 'table': self.geo_quote_name(model._meta.db_table),
+ 'column': self.geo_quote_name(field.column),
+ })
+ if field.spatial_index:
+ self.execute(self.sql_drop_spatial_index % {
+ 'index': self.quote_name(self._create_spatial_index_name(model, field)),
+ })
+ super(OracleGISSchemaEditor, self).remove_field(model, field)
+
+ def run_geometry_sql(self):
+ for sql in self.geometry_sql:
+ self.execute(sql)
+ self.geometry_sql = []
+
+ def _create_spatial_index_name(self, model, field):
+ # Oracle doesn't allow object names > 30 characters. Use this scheme
+ # instead of self._create_index_name() for backwards compatibility.
+ return truncate_name('%s_%s_id' % (model._meta.db_table, field.column), 30)
diff --git a/django/contrib/gis/tests/gis_migrations/test_operations.py b/django/contrib/gis/tests/gis_migrations/test_operations.py
index 32072eec86..c979317630 100644
--- a/django/contrib/gis/tests/gis_migrations/test_operations.py
+++ b/django/contrib/gis/tests/gis_migrations/test_operations.py
@@ -51,6 +51,17 @@ class OperationTests(TransactionTestCase):
)]
return self.apply_operations('gis', ProjectState(), operations)
+ def assertGeometryColumnsCount(self, expected_count):
+ table_name = "gis_neighborhood"
+ if connection.features.uppercases_column_names:
+ table_name = table_name.upper()
+ self.assertEqual(
+ GeometryColumns.objects.filter(**{
+ GeometryColumns.table_name_col(): table_name,
+ }).count(),
+ expected_count
+ )
+
def test_add_gis_field(self):
"""
Tests the AddField operation with a GIS-enabled column.
@@ -70,10 +81,7 @@ class OperationTests(TransactionTestCase):
# Test GeometryColumns when available
if HAS_GEOMETRY_COLUMNS:
- self.assertEqual(
- GeometryColumns.objects.filter(**{GeometryColumns.table_name_col(): "gis_neighborhood"}).count(),
- 2
- )
+ self.assertGeometryColumnsCount(2)
if self.has_spatial_indexes:
with connection.cursor() as cursor:
@@ -95,10 +103,7 @@ class OperationTests(TransactionTestCase):
# Test GeometryColumns when available
if HAS_GEOMETRY_COLUMNS:
- self.assertEqual(
- GeometryColumns.objects.filter(**{GeometryColumns.table_name_col(): "gis_neighborhood"}).count(),
- 0
- )
+ self.assertGeometryColumnsCount(0)
def test_create_model_spatial_index(self):
self.current_state = self.set_up_test_model()
diff --git a/docs/releases/1.7.1.txt b/docs/releases/1.7.1.txt
index 3b53534185..a1149d37cd 100644
--- a/docs/releases/1.7.1.txt
+++ b/docs/releases/1.7.1.txt
@@ -77,6 +77,9 @@ Bugfixes
* Added ``SchemaEditor`` for MySQL GIS backend so that spatial indexes will be
created for apps with migrations (:ticket:`23538`).
+* Added ``SchemaEditor`` for Oracle GIS backend so that spatial metadata and
+ indexes will be created for apps with migrations (:ticket:`23537`).
+
* Coerced the ``related_name`` model field option to unicode during migration
generation to generate migrations that work with both Python 2 and 3
(:ticket:`23455`).