]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Commit transaction after SNAPSHOT isolation change
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 8 Mar 2019 23:36:33 +0000 (18:36 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 9 Mar 2019 04:45:25 +0000 (23:45 -0500)
A commit() is emitted after an isolation level change to SNAPSHOT, as both
pyodbc and pymssql open an implicit transaction which blocks subsequent SQL
from being emitted in the current transaction.

Fixes: #4536
Change-Id: If3ba70f495bce2a35a873a3a72d1b30406e678c8

doc/build/changelog/unreleased_12/4536.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/4536.rst b/doc/build/changelog/unreleased_12/4536.rst
new file mode 100644 (file)
index 0000000..b216962
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+   :tags: bug, mssql
+   :tickets: 4536
+   :versions: 1.3.1
+
+   A commit() is emitted after an isolation level change to SNAPSHOT, as both
+   pyodbc and pymssql open an implicit transaction which blocks subsequent SQL
+   from being emitted in the current transaction.
index 2d883309d190d1f88b8f54c81a830a0a90327ac1..992b97188a04dde56183aa3a6107156610a4d09b 100644 (file)
@@ -2235,6 +2235,8 @@ class MSDialect(default.DefaultDialect):
         cursor = connection.cursor()
         cursor.execute("SET TRANSACTION ISOLATION LEVEL %s" % level)
         cursor.close()
+        if level == "SNAPSHOT":
+            connection.commit()
 
     def get_isolation_level(self, connection):
         if self.server_version_info < MS_2005_VERSION:
index 5f46c5377f9177b47b2ca10fbbcf594596306b31..431b20c7a13cc7a885e9bae248b0c2a1e8d87495 100644 (file)
@@ -435,6 +435,40 @@ class VersionDetectionTest(fixtures.TestBase):
             eq_(dialect._get_server_version_info(conn), expected)
 
 
+class RealIsolationLevelTest(fixtures.TestBase):
+    __only_on__ = "mssql"
+    __backend__ = True
+
+    @testing.provide_metadata
+    def test_isolation_level(self):
+        Table("test", self.metadata, Column("id", Integer)).create(
+            checkfirst=True
+        )
+
+        with testing.db.connect() as c:
+            default = testing.db.dialect.get_isolation_level(c.connection)
+
+        values = [
+            "READ UNCOMMITTED",
+            "READ COMMITTED",
+            "REPEATABLE READ",
+            "SERIALIZABLE",
+            "SNAPSHOT",
+        ]
+        for value in values:
+            with testing.db.connect() as c:
+                c.execution_options(isolation_level=value)
+
+                c.execute("SELECT TOP 10 * FROM test")
+
+                eq_(
+                    testing.db.dialect.get_isolation_level(c.connection), value
+                )
+
+        with testing.db.connect() as c:
+            eq_(testing.db.dialect.get_isolation_level(c.connection), default)
+
+
 class IsolationLevelDetectTest(fixtures.TestBase):
     def _fixture(self, view):
         class Error(Exception):