diff --git a/core/wren/src/wren/connector/mssql.py b/core/wren/src/wren/connector/mssql.py index 2dc38c79a4..0f87cea886 100644 --- a/core/wren/src/wren/connector/mssql.py +++ b/core/wren/src/wren/connector/mssql.py @@ -469,7 +469,9 @@ def _connect_mssql_pyodbc( for key, value in connect_kwargs.items(): connection_parts.append(f"{key}={_escape_odbc_value(str(value))}") - connection = pyodbc.connect(";".join(connection_parts)) + # pyodbc defaults to autocommit=False, which would leave this cached, + # long-lived connection in one never-committed transaction. + connection = pyodbc.connect(";".join(connection_parts), autocommit=True) _register_mssql_output_converters(connection) if statement_timeout is not None: diff --git a/core/wren/tests/unit/test_mssql_connection.py b/core/wren/tests/unit/test_mssql_connection.py index 083967caf2..7869dd76b3 100644 --- a/core/wren/tests/unit/test_mssql_connection.py +++ b/core/wren/tests/unit/test_mssql_connection.py @@ -228,6 +228,27 @@ def test_mssql_both_credentials_emits_uid_and_pwd() -> None: assert "Trusted_Connection" not in parts +def test_mssql_connects_with_autocommit() -> None: + """pyodbc defaults to autocommit=False, so every statement on this + long-lived, process-cached connection would otherwise run inside a + transaction that is never committed. Mirrors the other connectors + (redshift.py, canner.py, mysql.py) that already turn autocommit on.""" + fake = _FakePyodbc() + with patch("wren.connector.mssql.pyodbc", fake): + _connect_mssql_pyodbc( + host="h", + port="1433", + database="db", + user="alice", + password="secret", + driver="ODBC Driver 18 for SQL Server", + ) + + fake.connect.assert_called_once() + _args, kwargs = fake.connect.call_args + assert kwargs.get("autocommit") is True + + # --------------------------------------------------------------------------- # 3. statement_timeout validated before connect() # ---------------------------------------------------------------------------