oracle / python-cx_Oracle

Python interface to Oracle Database now superseded by python-oracledb
https://oracle.github.io/python-cx_Oracle
Other
888 stars 361 forks source link

weird numeric issue, not sure which side its on but did not occur in cx_oracle 5.x #77

Closed zzzeek closed 7 years ago

zzzeek commented 7 years ago

the test program below works without issue on all the 5.x cx_Oracle series, like 5.3, etc. When I run it on 6.0.2, I get random behavior, all seemingly related to the value "5748E+15". With outconverter set, I get just failures like:

AssertionError: set([Decimal('400000000'), Decimal('5749300000000000000'), Decimal('1521000000000000'), Decimal('100000000000')]) != set([Decimal('4E+8'), Decimal('5.748E+18'), Decimal('1E+11'), Decimal('1.521E+15')])

noting above, the exponent display is different, but that part is normal, the issue is the 5.748E value isn't coming back correctly - in my own test suites, sometimes it comes back as None also as though the outputtypehandler is getting skipped.

however more weirdly, if I use cursor.var(decimal.Decimal, ...) as cx_Oracle 6.0.x supports, then I get different errors each time, of the form:

decimal.InvalidOperation: Invalid literal for Decimal: '574A900000000000000' decimal.InvalidOperation: Invalid literal for Decimal: '574?700000000000000' decimal.InvalidOperation: Invalid literal for Decimal: '574:200000000000000' decimal.InvalidOperation: Invalid literal for Decimal: '574:900000000000000'

if I remove the "5748E+15" value, then everything works without failing.

this is Oracle 11.2 express. I'm not sure if this is some weird known Oracle thing but if I use cx_Oracle 5.x, it goes away. This may well be within the realm of, "this won't work anymore on Oracle express on a laptop" but looking to see if you have some idea of what changed and what could cause this.

import cx_Oracle
import decimal

conn = cx_Oracle.connect(
    user="scott",
    password="tiger",
    dsn=cx_Oracle.makedsn(
        "172.17.0.3", 1521, sid="xe",
    )
)

cursor = conn.cursor()

cursor.execute("""
CREATE TABLE t (
    x NUMERIC(27, 4)
)
""")
try:
    values = set([
        decimal.Decimal('4E+8'),
        decimal.Decimal("5748E+15"),  # <--- this number is causing the problem
        decimal.Decimal('1.521E+15'),
        decimal.Decimal('00000000000000.1E+12')
    ])

    cursor.executemany(
        "INSERT INTO t (x) VALUES (:x)",
        [{"x": value} for value in values]
    )

    def output_type_handler(cursor, name, defaultType,
                            size, precision, scale):
        return cursor.var(
            # using decimal.Decimal here on cx_Oracle 6.0.2 still breaks
            cx_Oracle.STRING,
            255,
            outconverter=decimal.Decimal,
            arraysize=cursor.arraysize)
    cursor.outputtypehandler = output_type_handler
    cursor.execute("SELECT x FROM t")

    received = set([
        row[0] for row in cursor.fetchall()
    ])

    print "cx_Oracle version: %r" % cx_Oracle.__version__
    print "Sent: %r   Received: %r" % (values, received)

    assert received == values, "%r != %r" % (received, values)

finally:
    cursor.execute("DROP TABLE t")
anthony-tuininga commented 7 years ago

Interesting! I'll take a look.

anthony-tuininga commented 7 years ago

Thanks for catching that. It was an intermittent issue due to the use of uninitialised data (only relevant with some numbers and only in some circumstances). I have corrected this in both ODPI-C and cx_Oracle.

zzzeek commented 7 years ago

awesome!! only some numbers as I observed :). great