diff --git a/django/contrib/gis/measure.py b/django/contrib/gis/measure.py index 42c49ce154..7f345329bc 100644 --- a/django/contrib/gis/measure.py +++ b/django/contrib/gis/measure.py @@ -46,6 +46,7 @@ class MeasureBase(object): """ val = 0.0 for unit, value in kwargs.iteritems(): + if not isinstance(value, float): value = float(value) if unit in self.UNITS: val += self.UNITS[unit] * value default_unit = unit diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index ce5e0f06f1..ac09b6f515 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -7,6 +7,7 @@ Requires cx_Oracle: http://cx-oracle.sourceforge.net/ import os import datetime import time +from decimal import Decimal # Oracle takes client-side character set encoding from the environment. os.environ['NLS_LANG'] = '.UTF8' @@ -287,6 +288,8 @@ class DatabaseWrapper(BaseDatabaseWrapper): pass if not cursor: cursor = FormatStylePlaceholderCursor(self.connection) + # Necessary to retrieve decimal values without rounding error. + cursor.numbersAsStrings = True # Default arraysize of 1 is highly sub-optimal. cursor.arraysize = 100 return cursor @@ -390,18 +393,57 @@ class FormatStylePlaceholderCursor(Database.Cursor): row = Database.Cursor.fetchone(self) if row is None: return row - return tuple([to_unicode(e) for e in row]) + return self._rowfactory(row) def fetchmany(self, size=None): if size is None: size = self.arraysize - return tuple([tuple([to_unicode(e) for e in r]) + return tuple([self._rowfactory(r) for r in Database.Cursor.fetchmany(self, size)]) def fetchall(self): - return tuple([tuple([to_unicode(e) for e in r]) + return tuple([self._rowfactory(r) for r in Database.Cursor.fetchall(self)]) + def _rowfactory(self, row): + # Cast numeric values as the appropriate Python type based upon the + # cursor description, and convert strings to unicode. + casted = [] + for value, desc in zip(row, self.description): + if value is not None and desc[1] is Database.NUMBER: + precision, scale = desc[4:6] + if scale == -127: + if precision == 0: + # NUMBER column: decimal-precision floating point + # This will normally be an integer from a sequence, + # but it could be a decimal value. + if '.' in value: + value = Decimal(value) + else: + value = int(value) + else: + # FLOAT column: binary-precision floating point. + # This comes from FloatField columns. + value = float(value) + elif precision > 0: + # NUMBER(p,s) column: decimal-precision fixed point. + # This comes from IntField and DecimalField columns. + if scale == 0: + value = int(value) + else: + value = Decimal(value) + elif '.' in value: + # No type information. This normally comes from a + # mathematical expression in the SELECT list. Guess int + # or Decimal based on whether it has a decimal point. + value = Decimal(value) + else: + value = int(value) + else: + value = to_unicode(value) + casted.append(value) + return tuple(casted) + def to_unicode(s): """