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 21:25:46 -0400
commit9d7a4ea20510a2e35fdeac21d67f3f4c17634c25 (patch)
tree7be393cb3bdd4637dd5714c4f0b6eb2760155a6b
parent692df414190b0cef0926bf50a758c222fefa6114 (diff)
[1.7.x] Fixed #23537 -- Added Oracle GIS SchemaEditor.
Thanks Shai Berger for review. Backport of a8f07530a7 from master
-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 701a9312b3..afa7ceee1e 100644
--- a/django/contrib/gis/db/backends/oracle/base.py
+++ b/django/contrib/gis/db/backends/oracle/base.py
@@ -2,6 +2,7 @@ from django.db.backends.oracle.base import DatabaseWrapper as OracleDatabaseWrap
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 DatabaseWrapper(OracleDatabaseWrapper):
@@ -10,3 +11,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 3af78929f8..5cffe21a17 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 convert_extent(self, clob):
if clob:
# Generally, Oracle returns a polygon for the extent -- however,
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 6fd1e54ace..92abf99b90 100644
--- a/django/contrib/gis/tests/gis_migrations/test_operations.py
+++ b/django/contrib/gis/tests/gis_migrations/test_operations.py
@@ -53,6 +53,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.
@@ -72,10 +83,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:
@@ -97,10 +105,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`).