Fixed #28411 -- Used cx_Oracle.Cursor.outputtypehandler instead of _rowfactory() on Oracle.
Thanks Tim Graham for the review.
This commit is contained in:
parent
dc738a0c76
commit
e06cab2600
|
@ -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)
|
|
||||||
|
|
Loading…
Reference in New Issue