Fixed #28411 -- Used cx_Oracle.Cursor.outputtypehandler instead of _rowfactory() on Oracle.

Thanks Tim Graham for the review.
This commit is contained in:
Mariusz Felisiak 2017-07-19 08:42:52 +02:00 committed by GitHub
parent dc738a0c76
commit e06cab2600
1 changed files with 29 additions and 51 deletions

View File

@ -395,18 +395,41 @@ class FormatStylePlaceholderCursor:
# The default for cx_Oracle < 5.3 is 50. # The default for cx_Oracle < 5.3 is 50.
self.cursor.arraysize = 100 self.cursor.arraysize = 100
@staticmethod
def _output_number_converter(value):
return decimal.Decimal(value) if '.' in value else int(value)
@staticmethod @staticmethod
def _output_type_handler(cursor, name, defaultType, length, precision, scale): def _output_type_handler(cursor, name, defaultType, length, precision, scale):
""" """
Called for each db column fetched from cursors. Return numbers as Called for each db column fetched from cursors. Return numbers as the
strings so that decimal values don't have rounding error. appropriate Python type.
""" """
if defaultType == Database.NUMBER: if defaultType == Database.NUMBER:
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.
outconverter = FormatStylePlaceholderCursor._output_number_converter
else:
# FLOAT column: binary-precision floating point.
# This comes from FloatField columns.
outconverter = float
elif precision > 0:
# NUMBER(p,s) column: decimal-precision fixed point.
# This comes from IntegerField and DecimalField columns.
outconverter = int if scale == 0 else decimal.Decimal
else:
# 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.
outconverter = FormatStylePlaceholderCursor._output_number_converter
return cursor.var( return cursor.var(
Database.STRING, Database.STRING,
size=255, size=255,
arraysize=cursor.arraysize, arraysize=cursor.arraysize,
outconverter=str, outconverter=outconverter,
) )
def _format_params(self, params): def _format_params(self, params):
@ -490,19 +513,13 @@ class FormatStylePlaceholderCursor:
self._guess_input_sizes(formatted) self._guess_input_sizes(formatted)
return self.cursor.executemany(query, [self._param_generator(p) for p in formatted]) return self.cursor.executemany(query, [self._param_generator(p) for p in formatted])
def fetchone(self):
row = self.cursor.fetchone()
if row is None:
return row
return _rowfactory(row, self.cursor)
def fetchmany(self, size=None): def fetchmany(self, size=None):
if size is None: if size is None:
size = self.arraysize size = self.arraysize
return tuple(_rowfactory(r, self.cursor) for r in self.cursor.fetchmany(size)) return tuple(self.cursor.fetchmany(size))
def fetchall(self): def fetchall(self):
return tuple(_rowfactory(r, self.cursor) for r in self.cursor.fetchall()) return tuple(self.cursor.fetchall())
def close(self): def close(self):
try: try:
@ -521,43 +538,4 @@ class FormatStylePlaceholderCursor:
return getattr(self.cursor, attr) return getattr(self.cursor, attr)
def __iter__(self): def __iter__(self):
return (_rowfactory(r, self.cursor) for r in self.cursor) return iter(self.cursor)
def _rowfactory(row, cursor):
# Cast numeric values as the appropriate Python type based upon the
# cursor description.
casted = []
for value, desc in zip(row, cursor.description):
if value is not None and desc[1] is Database.NUMBER:
precision = desc[4] or 0
scale = desc[5] or 0
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.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.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.Decimal(value)
else:
value = int(value)
casted.append(value)
return tuple(casted)