Describe the bug
The docstring on db_connection.connect says: timeout (int): The timeout for the connection attempt, in seconds.. However, it gets mixed up down the line with the query timeout.
sqlstate = 'HYT00'
ddbc_error = '[Microsoft][ODBC Driver 18 for SQL Server]Query timeout expired'
def raise_exception(sqlstate: str, ddbc_error: str) -> None:
"""
Raise a custom exception based on the given SQLSTATE code.
This function raises a custom exception based on the provided SQLSTATE code.
If the code is not found in the mapping, a generic DatabaseError is raised.
Args:
sqlstate (str): The SQLSTATE code to map to a custom exception.
ddbc_error (str): The DDBC error message.
Raises:
DatabaseError: If the SQLSTATE code is not found in the mapping.
"""
exception_class = sqlstate_to_exception(sqlstate, ddbc_error)
if exception_class:
logger.error(f"Raising exception: {exception_class}")
> raise exception_class
E mssql_python.exceptions.OperationalError: Driver Error: Timeout expired; DDBC Error: [Microsoft]Query timeout expired
To reproduce
I used the following test case to reproduce the issue
async def test_connection_timeout_repurposes_to_query_timeout(connection_string):
conn = mssql_python.connect(connection_string, timeout=3)
cursor = conn.cursor()
cursor.execute("WAITFOR DELAY '00:00:10'; SELECT 1") # times out after ~3s
val = cursor.fetchval()
assert 1 == val
Expected behavior
connect(timeout=3) should make the connection attempt time out after 3s.
Further technical details
Python version: 3.13
SQL Server version: Azure SQL Server
Operating system: Windows 11
Additional context
Discovered by Claude as I was migrating from pyodbc/aiodbc to mssql-python, I did A/B tests and can confirm that I believe it's an "unintended" bug in the library, hopefully I did by due diligence correctly!
Some more context from Claude
In connection.py, timeout is documented twice as a login timeout — "Login timeout in seconds. 0 means no timeout." (line 295) and "The timeout for the connection attempt, in seconds" in connect(). Its complete set of uses:
631: self._timeout = timeout # stored
928: return self._timeout # property documented as "the current query timeout"
950: self._timeout = value # setter: "Set the query timeout for all operations"
1464: cursor = Cursor(self, timeout=self._timeout)
It is never passed to SQL_ATTR_LOGIN_TIMEOUT. It silently becomes the query timeout. The same file's own example (line 354) shows attrs_before={ms.SQL_ATTR_LOGIN_TIMEOUT: 30} as the way to set a login timeout — so the mechanism is known, the parameter is just mis-wired.
Describe the bug
The docstring on
db_connection.connectsays:timeout (int): The timeout for the connection attempt, in seconds.. However, it gets mixed up down the line with the query timeout.To reproduce
I used the following test case to reproduce the issue
Expected behavior
connect(timeout=3) should make the connection attempt time out after 3s.
Further technical details
Python version: 3.13
SQL Server version: Azure SQL Server
Operating system: Windows 11
Additional context
Discovered by Claude as I was migrating from pyodbc/aiodbc to mssql-python, I did A/B tests and can confirm that I believe it's an "unintended" bug in the library, hopefully I did by due diligence correctly!
Some more context from Claude