[1.4.x] Fixed #19058 -- Fixed Oracle GIS crash

The problem is the same as in #10888 which was reintroduced when
bulk_insert was added. Thanks to Jani Tiainen for report, patch and
also testing the final patch on Oracle GIS.

Backpatch of 92d7f541da8b59520c833b19fbba52d3ecef2428
This commit is contained in:
Anssi Kääriäinen 2012-11-15 14:23:02 +02:00
parent fdb855e7b2
commit 25e041f270
4 changed files with 19 additions and 23 deletions

View File

@ -7,29 +7,7 @@ class GeoSQLCompiler(BaseGeoSQLCompiler, SQLCompiler):
pass pass
class SQLInsertCompiler(compiler.SQLInsertCompiler, GeoSQLCompiler): class SQLInsertCompiler(compiler.SQLInsertCompiler, GeoSQLCompiler):
def placeholder(self, field, val): pass
if field is None:
# A field value of None means the value is raw.
return val
elif hasattr(field, 'get_placeholder'):
# Some fields (e.g. geo fields) need special munging before
# they can be inserted.
ph = field.get_placeholder(val, self.connection)
if ph == 'NULL':
# If the placeholder returned is 'NULL', then we need to
# to remove None from the Query parameters. Specifically,
# cx_Oracle will assume a CHAR type when a placeholder ('%s')
# is used for columns of MDSYS.SDO_GEOMETRY. Thus, we use
# 'NULL' for the value, and remove None from the query params.
# See also #10888.
param_idx = self.query.columns.index(field.column)
params = list(self.query.params)
params.pop(param_idx)
self.query.params = tuple(params)
return ph
else:
# Return the common case for the placeholder
return '%s'
class SQLDeleteCompiler(compiler.SQLDeleteCompiler, GeoSQLCompiler): class SQLDeleteCompiler(compiler.SQLDeleteCompiler, GeoSQLCompiler):
pass pass

View File

@ -9,6 +9,7 @@
""" """
import re import re
from decimal import Decimal from decimal import Decimal
from itertools import izip
from django.db.backends.oracle.base import DatabaseOperations from django.db.backends.oracle.base import DatabaseOperations
from django.contrib.gis.db.backends.base import BaseSpatialOperations from django.contrib.gis.db.backends.base import BaseSpatialOperations
@ -287,3 +288,12 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
def spatial_ref_sys(self): def spatial_ref_sys(self):
from django.contrib.gis.db.backends.oracle.models import SpatialRefSys from django.contrib.gis.db.backends.oracle.models import SpatialRefSys
return SpatialRefSys return SpatialRefSys
def modify_insert_params(self, placeholders, params):
"""Drop out insert parameters for NULL placeholder. Needed for Oracle Spatial
backend due to #10888
"""
# This code doesn't work for bulk insert cases.
assert len(placeholders) == 1
return [[param for pholder,param
in izip(placeholders[0], params[0]) if pholder != 'NULL'], ]

View File

@ -874,6 +874,12 @@ class BaseDatabaseOperations(object):
conn = ' %s ' % connector conn = ' %s ' % connector
return conn.join(sub_expressions) return conn.join(sub_expressions)
def modify_insert_params(self, placeholders, params):
"""Allow modification of insert parameters. Needed for Oracle Spatial
backend due to #10888.
"""
return params
class BaseDatabaseIntrospection(object): class BaseDatabaseIntrospection(object):
""" """
This class encapsulates all backend-specific introspection utilities This class encapsulates all backend-specific introspection utilities

View File

@ -885,6 +885,8 @@ class SQLInsertCompiler(SQLCompiler):
[self.placeholder(field, v) for field, v in izip(fields, val)] [self.placeholder(field, v) for field, v in izip(fields, val)]
for val in values for val in values
] ]
# Oracle Spatial needs to remove some values due to #10888
params = self.connection.ops.modify_insert_params(placeholders, params)
if self.return_id and self.connection.features.can_return_id_from_insert: if self.return_id and self.connection.features.can_return_id_from_insert:
params = params[0] params = params[0]
col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column)) col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))