summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Fedoseev <fedoseev.sergey@gmail.com>2016-11-30 22:22:56 +0600
committerTim Graham <timograham@gmail.com>2016-11-30 11:22:56 -0500
commit4464b9b9ad9da921f8b50b4e7e26bb4233e05ca0 (patch)
tree754135766dd9c194add6b3bc43f87b8b7ef33c5c
parente17f40f4b55f5bf624a190e67a0c83af4be043b8 (diff)
Fixed #27556 -- Added Oracle support for IsValid function and isvalid lookup.
-rw-r--r--django/contrib/gis/db/backends/oracle/operations.py8
-rw-r--r--django/contrib/gis/db/models/functions.py6
-rw-r--r--django/contrib/gis/db/models/lookups.py7
-rw-r--r--docs/ref/contrib/gis/db-api.txt4
-rw-r--r--docs/ref/contrib/gis/functions.txt4
-rw-r--r--docs/releases/1.11.txt4
-rw-r--r--tests/gis_tests/geoapp/tests.py10
7 files changed, 32 insertions, 11 deletions
diff --git a/django/contrib/gis/db/backends/oracle/operations.py b/django/contrib/gis/db/backends/oracle/operations.py
index 639634e8e3..b4b04622c6 100644
--- a/django/contrib/gis/db/backends/oracle/operations.py
+++ b/django/contrib/gis/db/backends/oracle/operations.py
@@ -52,6 +52,10 @@ class SDORelate(SpatialOperator):
return super(SDORelate, self).as_sql(connection, lookup, template_params, sql_params)
+class SDOIsValid(SpatialOperator):
+ sql_template = "%%(func)s(%%(lhs)s, %s) = 'TRUE'" % DEFAULT_TOLERANCE
+
+
class OracleOperations(BaseSpatialOperations, DatabaseOperations):
name = 'oracle'
@@ -85,6 +89,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
'Difference': 'SDO_GEOM.SDO_DIFFERENCE',
'Distance': 'SDO_GEOM.SDO_DISTANCE',
'Intersection': 'SDO_GEOM.SDO_INTERSECTION',
+ 'IsValid': 'SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT',
'Length': 'SDO_GEOM.SDO_LENGTH',
'NumGeometries': 'SDO_UTIL.GETNUMELEM',
'NumPoints': 'SDO_UTIL.GETNUMVERTICES',
@@ -109,6 +114,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
'covers': SDOOperator(func='SDO_COVERS'),
'disjoint': SDODisjoint(),
'intersects': SDOOperator(func='SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()?
+ 'isvalid': SDOIsValid(func='SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT'),
'equals': SDOOperator(func='SDO_EQUAL'),
'exact': SDOOperator(func='SDO_EQUAL'),
'overlaps': SDOOperator(func='SDO_OVERLAPS'),
@@ -128,7 +134,7 @@ class OracleOperations(BaseSpatialOperations, DatabaseOperations):
unsupported_functions = {
'AsGeoJSON', 'AsGML', 'AsKML', 'AsSVG',
'BoundingCircle', 'Envelope',
- 'ForceRHR', 'GeoHash', 'IsValid', 'MakeValid', 'MemSize', 'Scale',
+ 'ForceRHR', 'GeoHash', 'MakeValid', 'MemSize', 'Scale',
'SnapToGrid', 'Translate',
}
diff --git a/django/contrib/gis/db/models/functions.py b/django/contrib/gis/db/models/functions.py
index 1929b2f7a1..1cec34d2bf 100644
--- a/django/contrib/gis/db/models/functions.py
+++ b/django/contrib/gis/db/models/functions.py
@@ -285,9 +285,13 @@ class Intersection(OracleToleranceMixin, GeoFuncWithGeoParam):
arity = 2
-class IsValid(GeoFunc):
+class IsValid(OracleToleranceMixin, GeoFunc):
output_field_class = BooleanField
+ def as_oracle(self, compiler, connection, **extra_context):
+ sql, params = super(IsValid, self).as_oracle(compiler, connection, **extra_context)
+ return "CASE %s WHEN 'TRUE' THEN 1 ELSE 0 END" % sql, params
+
class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
output_field_class = FloatField
diff --git a/django/contrib/gis/db/models/lookups.py b/django/contrib/gis/db/models/lookups.py
index 19a1b70e4e..8d4e8229e7 100644
--- a/django/contrib/gis/db/models/lookups.py
+++ b/django/contrib/gis/db/models/lookups.py
@@ -5,7 +5,7 @@ import re
from django.core.exceptions import FieldDoesNotExist
from django.db.models.constants import LOOKUP_SEP
from django.db.models.expressions import Col, Expression
-from django.db.models.lookups import BuiltinLookup, Lookup, Transform
+from django.db.models.lookups import Lookup, Transform
from django.db.models.sql.query import Query
from django.utils import six
@@ -352,15 +352,16 @@ class IntersectsLookup(GISLookup):
gis_lookups['intersects'] = IntersectsLookup
-class IsValidLookup(BuiltinLookup):
+class IsValidLookup(GISLookup):
lookup_name = 'isvalid'
+ sql_template = '%(func)s(%(lhs)s)'
def as_sql(self, compiler, connection):
if self.lhs.field.geom_type == 'RASTER':
raise ValueError('The isvalid lookup is only available on geometry fields.')
gis_op = connection.ops.gis_operators[self.lookup_name]
sql, params = self.process_lhs(compiler, connection)
- sql = '%(func)s(%(lhs)s)' % {'func': gis_op.func, 'lhs': sql}
+ sql, params = gis_op.as_sql(connection, self, {'func': gis_op.func, 'lhs': sql}, params)
if not self.rhs:
sql = 'NOT ' + sql
return sql, params
diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt
index 2b2e02ac16..670b23cd78 100644
--- a/docs/ref/contrib/gis/db-api.txt
+++ b/docs/ref/contrib/gis/db-api.txt
@@ -347,7 +347,7 @@ Lookup Type PostGIS Oracle MySQL [#]_ SpatiaLite
:lookup:`equals` X X X X C
:lookup:`exact` X X X X B
:lookup:`intersects` X X X X B
-:lookup:`isvalid` X X (LWGEOM)
+:lookup:`isvalid` X X X (LWGEOM)
:lookup:`overlaps` X X X X B
:lookup:`relate` X X X C
:lookup:`same_as` X X X X B
@@ -390,7 +390,7 @@ Function PostGIS Oracle MySQL SpatiaLite
:class:`ForceRHR` X
:class:`GeoHash` X X (LWGEOM)
:class:`Intersection` X X X (≥ 5.6.1) X
-:class:`IsValid` X X (LWGEOM)
+:class:`IsValid` X X X (LWGEOM)
:class:`Length` X X X X
:class:`MakeValid` X X (LWGEOM)
:class:`MemSize` X
diff --git a/docs/ref/contrib/gis/functions.txt b/docs/ref/contrib/gis/functions.txt
index b3f989126b..6a1bf10434 100644
--- a/docs/ref/contrib/gis/functions.txt
+++ b/docs/ref/contrib/gis/functions.txt
@@ -298,14 +298,14 @@ intersection between them.
.. versionadded:: 1.10
-*Availability*: PostGIS, SpatiaLite (LWGEOM)
+*Availability*: PostGIS, Oracle, SpatiaLite (LWGEOM)
Accepts a geographic field or expression and tests if the value is well formed.
Returns ``True`` if its value is a valid geometry and ``False`` otherwise.
.. versionchanged:: 1.11
- SpatiaLite support was added.
+ SpatiaLite and Oracle support was added.
``Length``
==========
diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt
index acc2fe10e2..7bf206c678 100644
--- a/docs/releases/1.11.txt
+++ b/docs/releases/1.11.txt
@@ -159,6 +159,10 @@ Minor features
:class:`~django.contrib.gis.db.models.functions.MakeValid` function, and
:lookup:`isvalid` lookup.
+* Added Oracle support for the
+ :class:`~django.contrib.gis.db.models.functions.IsValid` function and
+ :lookup:`isvalid` lookup.
+
:mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py
index 074162fe14..aede5950cd 100644
--- a/tests/gis_tests/geoapp/tests.py
+++ b/tests/gis_tests/geoapp/tests.py
@@ -295,8 +295,14 @@ class GeoLookupTest(TestCase):
def test_isvalid_lookup(self):
invalid_geom = fromstr('POLYGON((0 0, 0 1, 1 1, 1 0, 1 1, 1 0, 0 0))')
State.objects.create(name='invalid', poly=invalid_geom)
- self.assertEqual(State.objects.filter(poly__isvalid=False).count(), 1)
- self.assertEqual(State.objects.filter(poly__isvalid=True).count(), State.objects.count() - 1)
+ qs = State.objects.all()
+ if oracle:
+ # Kansas has adjacent vertices with distance 6.99244813842e-12
+ # which is smaller than the default Oracle tolerance.
+ qs = qs.exclude(name='Kansas')
+ self.assertEqual(State.objects.filter(name='Kansas', poly__isvalid=False).count(), 1)
+ self.assertEqual(qs.filter(poly__isvalid=False).count(), 1)
+ self.assertEqual(qs.filter(poly__isvalid=True).count(), qs.count() - 1)
@skipUnlessDBFeature("supports_left_right_lookups")
def test_left_right_lookups(self):