From: Mike Bayer Date: Thu, 28 Sep 2017 13:39:54 +0000 (-0400) Subject: Add full list of pyodbc error codes for MSSQL X-Git-Tag: rel_1_1_15~24^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=af70c3022f233f8cba00619a23d1f4a4173ad9e7;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Add full list of pyodbc error codes for MSSQL Moved the SQL server error codes out of connnectors/pyodbc.py and into mssql/pyodbc.py. Added complete list of odbc-related disconnect codes. Change-Id: Icd84a920dbfa1f188847f859654ff6f7a48170f1 Fixes: #4095 (cherry picked from commit 6a38697261e271b3600d8f1bbc56f663e5ee9890) --- diff --git a/doc/build/changelog/unreleased_11/4095.rst b/doc/build/changelog/unreleased_11/4095.rst new file mode 100644 index 0000000000..17286fa253 --- /dev/null +++ b/doc/build/changelog/unreleased_11/4095.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, mssql + :tickets: 4095 + :versions: 1.2.0b3 + + Added a full range of "connection closed" exception codes to the + PyODBC dialect for SQL Server, including '08S01', '01002', '08003', + '08007', '08S02', '08001', 'HYT00', 'HY010'. Previously, only '08S01' + was covered. \ No newline at end of file diff --git a/lib/sqlalchemy/connectors/pyodbc.py b/lib/sqlalchemy/connectors/pyodbc.py index ee8445dae4..c5d46a8110 100644 --- a/lib/sqlalchemy/connectors/pyodbc.py +++ b/lib/sqlalchemy/connectors/pyodbc.py @@ -124,8 +124,6 @@ class PyODBCConnector(Connector): if isinstance(e, self.dbapi.ProgrammingError): return "The cursor's connection has been closed." in str(e) or \ 'Attempt to use a closed connection.' in str(e) - elif isinstance(e, self.dbapi.Error): - return '[08S01]' in str(e) else: return False diff --git a/lib/sqlalchemy/dialects/mssql/pyodbc.py b/lib/sqlalchemy/dialects/mssql/pyodbc.py index c6368f9696..5545e005c3 100644 --- a/lib/sqlalchemy/dialects/mssql/pyodbc.py +++ b/lib/sqlalchemy/dialects/mssql/pyodbc.py @@ -289,4 +289,14 @@ class MSDialect_pyodbc(PyODBCConnector, MSDialect): version.append(n) return tuple(version) + def is_disconnect(self, e, connection, cursor): + if isinstance(e, self.dbapi.Error): + for code in ( + '08S01', '01002', '08003', '08007', + '08S02', '08001', 'HYT00', 'HY010'): + if code in str(e): + return True + return super(MSDialect_pyodbc, self).is_disconnect( + e, connection, cursor) + dialect = MSDialect_pyodbc diff --git a/test/dialect/mssql/test_engine.py b/test/dialect/mssql/test_engine.py index 1021f4e99e..b69e62572e 100644 --- a/test/dialect/mssql/test_engine.py +++ b/test/dialect/mssql/test_engine.py @@ -202,6 +202,35 @@ class ParseConnectTest(fixtures.TestBase): eq_(dialect.is_disconnect("not an error", None, None), False) + def test_pyodbc_disconnect(self): + dialect = pyodbc.dialect() + + class MockDBAPIError(Exception): + pass + + class MockProgrammingError(MockDBAPIError): + pass + + dialect.dbapi = Mock( + Error=MockDBAPIError, ProgrammingError=MockProgrammingError) + + for error in [ + MockDBAPIError("[%s] some pyodbc message" % code) + for code in [ + '08S01', '01002', '08003', '08007', + '08S02', '08001', 'HYT00', 'HY010'] + ] + [ + MockProgrammingError(message) + for message in [ + "(some pyodbc stuff) The cursor's connection has been closed.", + "(some pyodbc stuff) Attempt to use a closed connection." + ] + ]: + eq_(dialect.is_disconnect(error, None, None), True) + + eq_(dialect.is_disconnect( + MockProgrammingError("not an error"), None, None), False) + @testing.requires.mssql_freetds def test_bad_freetds_warning(self): engine = engines.testing_engine()