~ubuntu-branches/ubuntu/quantal/python-django/quantal-security

« back to all changes in this revision

Viewing changes to django/contrib/gis/db/backend/oracle/query.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2010-05-21 07:52:55 UTC
  • mfrom: (1.3.6 upstream)
  • mto: This revision was merged to the branch mainline in revision 28.
  • Revision ID: james.westby@ubuntu.com-20100521075255-ii78v1dyfmyu3uzx
Tags: upstream-1.2
ImportĀ upstreamĀ versionĀ 1.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
 This module contains the spatial lookup types, and the `get_geo_where_clause`
3
 
 routine for Oracle Spatial.
4
 
 
5
 
 Please note that WKT support is broken on the XE version, and thus
6
 
 this backend will not work on such platforms.  Specifically, XE lacks 
7
 
 support for an internal JVM, and Java libraries are required to use 
8
 
 the WKT constructors.
9
 
"""
10
 
import re
11
 
from decimal import Decimal
12
 
from django.db import connection
13
 
from django.contrib.gis.db.backend.util import SpatialFunction
14
 
from django.contrib.gis.measure import Distance
15
 
qn = connection.ops.quote_name
16
 
 
17
 
# The GML, distance, transform, and union procedures.
18
 
AREA = 'SDO_GEOM.SDO_AREA'
19
 
ASGML = 'SDO_UTIL.TO_GMLGEOMETRY'
20
 
CENTROID = 'SDO_GEOM.SDO_CENTROID'
21
 
DIFFERENCE = 'SDO_GEOM.SDO_DIFFERENCE'
22
 
DISTANCE = 'SDO_GEOM.SDO_DISTANCE'
23
 
EXTENT = 'SDO_AGGR_MBR'
24
 
INTERSECTION = 'SDO_GEOM.SDO_INTERSECTION'
25
 
LENGTH = 'SDO_GEOM.SDO_LENGTH'
26
 
NUM_GEOM = 'SDO_UTIL.GETNUMELEM'
27
 
NUM_POINTS = 'SDO_UTIL.GETNUMVERTICES'
28
 
POINT_ON_SURFACE = 'SDO_GEOM.SDO_POINTONSURFACE'
29
 
SYM_DIFFERENCE = 'SDO_GEOM.SDO_XOR'
30
 
TRANSFORM = 'SDO_CS.TRANSFORM'
31
 
UNION = 'SDO_GEOM.SDO_UNION'
32
 
UNIONAGG = 'SDO_AGGR_UNION'
33
 
 
34
 
# We want to get SDO Geometries as WKT because it is much easier to 
35
 
# instantiate GEOS proxies from WKT than SDO_GEOMETRY(...) strings.  
36
 
# However, this adversely affects performance (i.e., Java is called 
37
 
# to convert to WKT on every query).  If someone wishes to write a 
38
 
# SDO_GEOMETRY(...) parser in Python, let me know =)
39
 
GEOM_SELECT = 'SDO_UTIL.TO_WKTGEOMETRY(%s)'
40
 
 
41
 
#### Classes used in constructing Oracle spatial SQL ####
42
 
class SDOOperation(SpatialFunction):
43
 
    "Base class for SDO* Oracle operations."
44
 
    def __init__(self, func, **kwargs):
45
 
        kwargs.setdefault('operator', '=')
46
 
        kwargs.setdefault('result', 'TRUE')
47
 
        kwargs.setdefault('end_subst', ") %s '%s'")
48
 
        super(SDOOperation, self).__init__(func, **kwargs)
49
 
 
50
 
class SDODistance(SpatialFunction):
51
 
    "Class for Distance queries."
52
 
    def __init__(self, op, tolerance=0.05):
53
 
        super(SDODistance, self).__init__(DISTANCE, end_subst=', %s) %%s %%s' % tolerance, 
54
 
                                          operator=op, result='%%s')
55
 
 
56
 
class SDOGeomRelate(SpatialFunction):
57
 
    "Class for using SDO_GEOM.RELATE."
58
 
    def __init__(self, mask, tolerance=0.05):
59
 
        # SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance.
60
 
        # Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE').
61
 
        end_subst = "%s%s) %s '%s'" % (', %%s, ', tolerance, '=', mask)
62
 
        beg_subst = "%%s(%%s, '%s'" % mask 
63
 
        super(SDOGeomRelate, self).__init__('SDO_GEOM.RELATE', beg_subst=beg_subst, end_subst=end_subst)
64
 
 
65
 
class SDORelate(SpatialFunction):
66
 
    "Class for using SDO_RELATE."
67
 
    masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON'
68
 
    mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I)
69
 
    def __init__(self, mask):
70
 
        func = 'SDO_RELATE'
71
 
        if not self.mask_regex.match(mask):
72
 
            raise ValueError('Invalid %s mask: "%s"' % (func, mask))
73
 
        super(SDORelate, self).__init__(func, end_subst=", 'mask=%s') = 'TRUE'" % mask)
74
 
 
75
 
#### Lookup type mapping dictionaries of Oracle spatial operations ####
76
 
 
77
 
# Valid distance types and substitutions
78
 
dtypes = (Decimal, Distance, float, int, long)
79
 
DISTANCE_FUNCTIONS = {
80
 
    'distance_gt' : (SDODistance('>'), dtypes),
81
 
    'distance_gte' : (SDODistance('>='), dtypes),
82
 
    'distance_lt' : (SDODistance('<'), dtypes),
83
 
    'distance_lte' : (SDODistance('<='), dtypes),
84
 
    'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE', 
85
 
                              beg_subst="%s(%s, %%s, 'distance=%%s'"), dtypes),
86
 
    }
87
 
 
88
 
ORACLE_GEOMETRY_FUNCTIONS = {
89
 
    'contains' : SDOOperation('SDO_CONTAINS'),
90
 
    'coveredby' : SDOOperation('SDO_COVEREDBY'),
91
 
    'covers' : SDOOperation('SDO_COVERS'),
92
 
    'disjoint' : SDOGeomRelate('DISJOINT'),
93
 
    'intersects' : SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()?
94
 
    'equals' : SDOOperation('SDO_EQUAL'),
95
 
    'exact' : SDOOperation('SDO_EQUAL'),
96
 
    'overlaps' : SDOOperation('SDO_OVERLAPS'),
97
 
    'same_as' : SDOOperation('SDO_EQUAL'),
98
 
    'relate' : (SDORelate, basestring), # Oracle uses a different syntax, e.g., 'mask=inside+touch'
99
 
    'touches' : SDOOperation('SDO_TOUCH'),
100
 
    'within' : SDOOperation('SDO_INSIDE'),
101
 
    }
102
 
ORACLE_GEOMETRY_FUNCTIONS.update(DISTANCE_FUNCTIONS)
103
 
 
104
 
# This lookup type does not require a mapping.
105
 
MISC_TERMS = ['isnull']
106
 
 
107
 
# Acceptable lookup types for Oracle spatial.
108
 
ORACLE_SPATIAL_TERMS  = ORACLE_GEOMETRY_FUNCTIONS.keys()
109
 
ORACLE_SPATIAL_TERMS += MISC_TERMS
110
 
ORACLE_SPATIAL_TERMS = dict((term, None) for term in ORACLE_SPATIAL_TERMS) # Making dictionary for fast lookups
111
 
 
112
 
#### The `get_geo_where_clause` function for Oracle ####
113
 
def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
114
 
    "Returns the SQL WHERE clause for use in Oracle spatial SQL construction."
115
 
    # Getting the quoted table name as `geo_col`.
116
 
    geo_col = '%s.%s' % (qn(table_alias), qn(name))
117
 
 
118
 
    # See if a Oracle Geometry function matches the lookup type next
119
 
    lookup_info = ORACLE_GEOMETRY_FUNCTIONS.get(lookup_type, False)
120
 
    if lookup_info:
121
 
        # Lookup types that are tuples take tuple arguments, e.g., 'relate' and 
122
 
        # 'dwithin' lookup types.
123
 
        if isinstance(lookup_info, tuple):
124
 
            # First element of tuple is lookup type, second element is the type
125
 
            # of the expected argument (e.g., str, float)
126
 
            sdo_op, arg_type = lookup_info
127
 
 
128
 
            # Ensuring that a tuple _value_ was passed in from the user
129
 
            if not isinstance(geo_annot.value, tuple):
130
 
                raise TypeError('Tuple required for `%s` lookup type.' % lookup_type)
131
 
            if len(geo_annot.value) != 2: 
132
 
                raise ValueError('2-element tuple required for %s lookup type.' % lookup_type)
133
 
            
134
 
            # Ensuring the argument type matches what we expect.
135
 
            if not isinstance(geo_annot.value[1], arg_type):
136
 
                raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(geo_annot.value[1])))
137
 
 
138
 
            if lookup_type == 'relate':
139
 
                # The SDORelate class handles construction for these queries, 
140
 
                # and verifies the mask argument.
141
 
                return sdo_op(geo_annot.value[1]).as_sql(geo_col)
142
 
            else:
143
 
                # Otherwise, just call the `as_sql` method on the SDOOperation instance.
144
 
                return sdo_op.as_sql(geo_col)
145
 
        else:
146
 
            # Lookup info is a SDOOperation instance, whose `as_sql` method returns
147
 
            # the SQL necessary for the geometry function call. For example:  
148
 
            #  SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE'
149
 
            return lookup_info.as_sql(geo_col)
150
 
    elif lookup_type == 'isnull':
151
 
        # Handling 'isnull' lookup type
152
 
        return "%s IS %sNULL" % (geo_col, (not geo_annot.value and 'NOT ' or ''))
153
 
 
154
 
    raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))