]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fix UnboundLocalError in mssql during isolation level grab
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 18 Jun 2018 14:12:56 +0000 (17:12 +0300)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 25 Jun 2018 22:35:41 +0000 (18:35 -0400)
Fixed issue within the SQL Server dialect under Python 3 where when running
against a non-standard SQL server database that does not contain either the
"sys.dm_exec_sessions" or "sys.dm_pdw_nodes_exec_sessions" views, leading
to a failure to fetch the isolation level, the error raise would fail due
to an UnboundLocalError.

Fixes: #4273
Co-authored-by: wikiped <wikiped@yandex.ru>
Change-Id: I39877c1f65f9cf8602fb1dceaf03072357759564

doc/build/changelog/unreleased_12/4273.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/mssql/base.py
test/dialect/mssql/test_engine.py

diff --git a/doc/build/changelog/unreleased_12/4273.rst b/doc/build/changelog/unreleased_12/4273.rst
new file mode 100644 (file)
index 0000000..ec8b95c
--- /dev/null
@@ -0,0 +1,11 @@
+.. change::
+    :tags: bug, mssql, py3k
+    :tickets: 4273
+
+    Fixed issue within the SQL Server dialect under Python 3 where when running
+    against a non-standard SQL server database that does not contain either the
+    "sys.dm_exec_sessions" or "sys.dm_pdw_nodes_exec_sessions" views, leading
+    to a failure to fetch the isolation level, the error raise would fail due
+    to an UnboundLocalError.
+
+
index 2975a6c69336935ec827db4d30906bb2c16fd58d..bc3905535e4da4b546e1d67ebf51f851ac8d481f 100644 (file)
@@ -1897,6 +1897,8 @@ class MSDialect(default.DefaultDialect):
             raise NotImplementedError(
                 "Can't fetch isolation level prior to SQL Server 2005")
 
+        last_error = None
+
         views = ("sys.dm_exec_sessions", "sys.dm_pdw_nodes_exec_sessions")
         for view in views:
             cursor = connection.cursor()
@@ -1914,19 +1916,22 @@ class MSDialect(default.DefaultDialect):
                   """ % view)
                 val = cursor.fetchone()[0]
             except self.dbapi.Error as err:
+                # Python3 scoping rules
+                last_error = err
                 continue
             else:
                 return val.upper()
             finally:
                 cursor.close()
+        else:
+            util.warn(
+                "Could not fetch transaction isolation level, "
+                "tried views: %s; final error was: %s" % (views, last_error))
 
-        util.warn(
-            "Could not fetch transaction isolation level, "
-            "tried views: %s; final error was: %s" % (views, err))
-        raise NotImplementedError(
-            "Can't fetch isolation level on this particular "
-            "SQL Server version"
-        )
+            raise NotImplementedError(
+                "Can't fetch isolation level on this particular "
+                "SQL Server version"
+            )
 
     def initialize(self, connection):
         super(MSDialect, self).initialize(connection)
index db54381cf636fd2a73a5fee14eabd41f3da6a870..3c6c479f974a9071bd0fdcb05a7bac40caa5d658 100644 (file)
@@ -6,8 +6,10 @@ from sqlalchemy.dialects.mssql import pyodbc, pymssql, adodbapi
 from sqlalchemy.engine import url
 from sqlalchemy.testing import fixtures
 from sqlalchemy import testing
-from sqlalchemy.testing import assert_raises_message, assert_warnings
+from sqlalchemy.testing import assert_raises_message, \
+    assert_warnings, expect_warnings
 from sqlalchemy.testing.mock import Mock
+from sqlalchemy.dialects.mssql import base
 
 
 class ParseConnectTest(fixtures.TestBase):
@@ -301,4 +303,61 @@ class VersionDetectionTest(fixtures.TestBase):
             eq_(
                 dialect._get_server_version_info(conn),
                 expected
-            )
\ No newline at end of file
+            )
+
+
+class IsolationLevelDetectTest(fixtures.TestBase):
+
+    def _fixture(self, view):
+        class Error(Exception):
+            pass
+
+        dialect = pyodbc.MSDialect_pyodbc()
+        dialect.dbapi = Mock(Error=Error)
+        dialect.server_version_info = base.MS_2012_VERSION
+
+        result = []
+
+        def fail_on_exec(stmt, ):
+            if view is not None and view in stmt:
+                result.append(('SERIALIZABLE', ))
+            else:
+                raise Error("that didn't work")
+
+        connection = Mock(
+            cursor=Mock(
+                return_value=Mock(
+                    execute=fail_on_exec,
+                    fetchone=lambda: result[0]
+                ),
+            )
+        )
+
+        return dialect, connection
+
+    def test_dm_pdw_nodes(self):
+        dialect, connection = self._fixture("dm_pdw_nodes_exec_sessions")
+
+        eq_(
+            dialect.get_isolation_level(connection),
+            "SERIALIZABLE"
+        )
+
+    def test_exec_sessions(self):
+        dialect, connection = self._fixture("exec_sessions")
+
+        eq_(
+            dialect.get_isolation_level(connection),
+            "SERIALIZABLE"
+        )
+
+    def test_not_supported(self):
+        dialect, connection = self._fixture(None)
+
+        with expect_warnings("Could not fetch transaction isolation level"):
+            assert_raises_message(
+                NotImplementedError,
+                "Can't fetch isolation",
+                dialect.get_isolation_level, connection
+            )
+