mkleehammer / pyodbc

Python ODBC bridge
https://github.com/mkleehammer/pyodbc/wiki
MIT No Attribution
2.88k stars 562 forks source link

pyodbc - fetchone() crash - Only 4.0.38 issue (other versions work fine) #1196

Closed jvdalen closed 1 year ago

jvdalen commented 1 year ago

Environment

Issue

Get a none-defined error (python crashes)

Code works in 4.0.35 and before. Stopped working after updating to 4.0.38. Works again after downgrading to 4.0.35.

---Some test code that reproduces it for me:

    connection_string = os.environ["SQL_CONNECTION_STRING"]
    with pyodbc.connect(connection_string) as conn:
        cursor = conn.cursor()
        cursor. Execute("SELECT TOP 1 * FROM table")
        logging.info("Executed query")
        row = cursor.fetchone() 
        #loggin the row data
        logging.info("Fetched row")
        logging.info(f"Row: {row}")

Error: Language Worker Process exited. Pid=1516. / python exited with code -1073741819 (0xC0000005).

If running above without fetchone() no issues.


My first ever "bug report". Apologies if it is not according to the standard. Done my best to make it useful.

v-chojas commented 1 year ago

Could you run in a debugger and show where the crash happens?

gordthompson commented 1 year ago

@jvdalen - Good first effort! Thanks for reporting.

You can get the driver information from the connection, e.g., on my Ubuntu box I get

cnxn = pyodbc.connect("DSN=mssql_199;UID=scott;PWD=tiger^5HHH")
print(cnxn.getinfo(pyodbc.SQL_DRIVER_NAME))  # libmsodbcsql-17.10.so.2.1
print(cnxn.getinfo(pyodbc.SQL_DRIVER_VER))  # 17.10.0002

(BTW, this works fine for me)

import pyodbc

print(pyodbc.version)  # 4.0.38

cnxn = pyodbc.connect("DSN=mssql_199;UID=scott;PWD=tiger^5HHH")
print(cnxn.getinfo(pyodbc.SQL_DRIVER_NAME))  # libmsodbcsql-17.10.so.2.1
print(cnxn.getinfo(pyodbc.SQL_DRIVER_VER))  # 17.10.0002

crsr = cnxn.cursor()
result = crsr.execute("SELECT 1 AS foo").fetchone()
print(result)  # (1,)
Jskarie commented 1 year ago

I'm also having problems with crashing on 4.0.38. Reverting fixes the issue. I'm getting an exit code 245. The error doesn't show up until I try to pull the values. it crashes on the line with the logger. I can't even catch the error...

row = self.execute_sql(*args).fetchone() logger.info(f"sql: {args} time:{round(time.time() - t0, 2)} out:{row}") return row

driver:IBM i Access ODBC Driver

dr-rodriguez commented 1 year ago

My tests running in a docker container started failing when updating pyodbc to 4.0.38. I haven't been able to fully track down what the issue is as it reports it as a Python segmentation fault error. Reverting to 4.0.35 fixed the issue.

Here's what I see when I check the database result a few times:

>>> con = pyodbc.connect(dsn=DSN, database=DB, autocommit=True, trusted_connection='yes', app='pyCAOM_unittest')
>>> print(con.getinfo(pyodbc.SQL_DRIVER_NAME))
libmsodbcsql-17.10.so.2.1
>>> print(con.getinfo(pyodbc.SQL_DRIVER_VER)) 
17.10.0002
>>> 
>>> query = 'select @@servername'
>>> db_result = con.execute(query).fetchall()
>>> print(db_result)
[('TWMASTDB9',)]
>>> print(db_result)
[('[_b',)]
>>> print(db_result)
[''\'\\\'\\S',)]
>>> print(db_result)
Segmentation fault (core dumped)

With pyodbc 4.0.35 my value of db_result doesn't change with each print statement.

gordthompson commented 1 year ago

I am able to reproduce the results reported by @dr-rodriguez . With 4.0.38 the value of db_result changes each time it is printed, but with 4.0.35 it remains consistent.

4.0.35

import pyodbc

print(pyodbc.version)  # 4.0.35

cnxn = pyodbc.connect("DSN=mssql_199;UID=scott;PWD=tiger^5HHH")
print(cnxn.getinfo(pyodbc.SQL_DRIVER_NAME))  # libmsodbcsql-17.10.so.2.1
print(cnxn.getinfo(pyodbc.SQL_DRIVER_VER))  # 17.10.0002

crsr = cnxn.cursor()
db_result = crsr.execute("SELECT @@servername").fetchall()
print(db_result)  # [('a3924f40a328', )]
print(db_result)  # [('a3924f40a328', )]
print(db_result)  # [('a3924f40a328', )]
print(db_result)  # [('a3924f40a328', )]

4.0.38

import pyodbc

print(pyodbc.version)  # 4.0.38

cnxn = pyodbc.connect("DSN=mssql_199;UID=scott;PWD=tiger^5HHH")
print(cnxn.getinfo(pyodbc.SQL_DRIVER_NAME))  # libmsodbcsql-17.10.so.2.1
print(cnxn.getinfo(pyodbc.SQL_DRIVER_VER))  # 17.10.0002

crsr = cnxn.cursor()
db_result = crsr.execute("SELECT @@servername").fetchall()
print(db_result)  # [('a3924f40a328', )]
print(db_result)  # [('(392',)]
print(db_result)  # [("('(3",)]
print(db_result)  # [('("(\'',)]
gordthompson commented 1 year ago

Additional info: This seems to only affect string values

db_result = crsr.execute("SELECT N'hello world' AS foo").fetchall()
print(db_result)  # [('hello world',)]
print(db_result)  # [('(ell',)]
print(db_result)  # [("('(e",)]
print(db_result)  # [('("(\'',)]
db_result = crsr.execute("SELECT 'hello world' AS foo").fetchall()
print(db_result)  # [('hello world',)]
print(db_result)  # [('(ell',)]
print(db_result)  # [("('(e",)]
print(db_result)  # [('("(\'',)]

but not other types

db_result = crsr.execute("SELECT 1 AS foo").fetchall()
print(db_result)  # [(1,)]
print(db_result)  # [(1,)]
print(db_result)  # [(1,)]
db_result = crsr.execute("SELECT 3.14 AS foo").fetchall()
print(db_result)  # [(Decimal('3.14'),)]
print(db_result)  # [(Decimal('3.14'),)]
print(db_result)  # [(Decimal('3.14'),)]
db_result = crsr.execute("SELECT CAST('2023-04-12 13:14:15' AS datetime2) AS foo").fetchall()
print(db_result)  # [(datetime.datetime(2023, 4, 12, 13, 14, 15),)]
print(db_result)  # [(datetime.datetime(2023, 4, 12, 13, 14, 15),)]
print(db_result)  # [(datetime.datetime(2023, 4, 12, 13, 14, 15),)]
v-chojas commented 1 year ago

I suspect the change that caused it is https://github.com/mkleehammer/pyodbc/commit/615ebddc266e1f8d4cbb741291011ee4a6664811

jvdalen commented 1 year ago
print(cnxn.getinfo(pyodbc.SQL_DRIVER_NAME))  # libmsodbcsql-17.10.so.2.1
print(cnxn.getinfo(pyodbc.SQL_DRIVER_VER))  # 17.10.0002

@gordthompson Thanks!

Driver version: 17.10.0003 Driver: msodbcsql17.dll

@v-chojas, let me know if you still need additional debug info (VSCode doesn't seem to give me more info than what provided). With the above comments from others, if still more info is required, please let me know and I will see if I can get more debugger ifno.

Jskarie commented 1 year ago

I suspect the change that caused it is 615ebdd

That is likely correct. I can run 4829107, without issues where as 615ebdd fails.

@jvdalen you can test this via commit prior to the issue for me pip install pyodbc@git+https://github.com/mkleehammer/pyodbc@4829107 commit with the issue pip install pyodbc@git+https://github.com/mkleehammer/pyodbc@615ebdd

Thank you!

jvdalen commented 1 year ago

@Jskarie,

I have just tested as per the above. That is indeed correct. The second one reproduces the error, the first doesn't.

615ebdd seems to be the culprit.

mkleehammer commented 1 year ago

I forgot to include the issue number in the commit: 4c385cf58bae68e517029d0052a50722ff17ad65

There is a clear reference count bug in the new code. I've just fixed it and will make a new release. This is an embarrasing bug. I'm very surprised none of the unit tests caught this.

mkleehammer commented 1 year ago

FYI: I have yanked 4.0.38 from PyPI. 4.0.39 will be uploaded soon. The AppVeyor tests take a while. Once the current set completes I'll bump the version, push, wait for those tests to complete (as they'll have the version tag), then release.

Thanks to everyone for reporting this.

mkleehammer commented 1 year ago

Uploading 4.0.39 now...

dr-rodriguez commented 1 year ago

Thanks for this quick fix! My own tests are now passing with 4.0.39

Jskarie commented 1 year ago

Thank you! I appreciate the work you put into maintaining this project.

jvdalen commented 1 year ago

Awesome and thanks for resolving this!