From: Mike Bayer Date: Tue, 15 Mar 2016 21:11:08 +0000 (-0400) Subject: - changelog and docs for #3332, fixes #3332 X-Git-Tag: rel_1_1_0b1~97 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=31f96c27a5fea302358ba580313a2f742c12b83d;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - changelog and docs for #3332, fixes #3332 - make docs for isolation level more consistent between postgresql and mysql - move mysql autocommit tests --- diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst index ef52c4bbef..e6dac92657 100644 --- a/doc/build/changelog/changelog_11.rst +++ b/doc/build/changelog/changelog_11.rst @@ -21,6 +21,18 @@ .. changelog:: :version: 1.1.0b1 + .. change:: + :tags: feature, mysql + :tickets: 3332 + + Added support for "autocommit" on MySQL drivers, via the + AUTOCOMMIT isolation level setting. Pull request courtesy + Roman Podoliaka. + + .. seealso:: + + :ref:`change_3332` + .. change:: :tags: bug, orm :tickets: 3677 diff --git a/doc/build/changelog/migration_11.rst b/doc/build/changelog/migration_11.rst index cbf213d440..3f40c7278b 100644 --- a/doc/build/changelog/migration_11.rst +++ b/doc/build/changelog/migration_11.rst @@ -1861,6 +1861,25 @@ common to both MySQL and Postgresql. :ticket:`3547` +.. _change_3332: + +Added support for AUTOCOMMIT "isolation level" +---------------------------------------------- + +The MySQL dialect now accepts the value "AUTOCOMMIT" for the +:paramref:`.create_engine.isolation_level` and +:paramref:`.Connection.execution_options.isolation_level` +parameters:: + + connection = engine.connect() + connection = connection.execution_options( + isolation_level="AUTOCOMMIT" + ) + +The isolation level makes use of the various "autocommit" attributes +provided by most MySQL DBAPIs. + +:ticket:`3332` .. _change_mysql_3216: diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index b040701623..b85e32db01 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -111,19 +111,45 @@ to be used. Transaction Isolation Level --------------------------- -:func:`.create_engine` accepts an :paramref:`.create_engine.isolation_level` -parameter which results in the command ``SET SESSION -TRANSACTION ISOLATION LEVEL `` being invoked for -every new connection. Valid values for this parameter are -``READ COMMITTED``, ``READ UNCOMMITTED``, -``REPEATABLE READ``, and ``SERIALIZABLE``:: +All MySQL dialects support setting of transaction isolation level +both via a dialect-specific parameter :paramref:`.create_engine.isolation_level` +accepted by :func:`.create_engine`, +as well as the :paramref:`.Connection.execution_options.isolation_level` +argument as passed to :meth:`.Connection.execution_options`. +This feature works by issuing the command +``SET SESSION TRANSACTION ISOLATION LEVEL `` for +each new connection. For the special AUTOCOMMIT isolation level, DBAPI-specific +techniques are used. + +To set isolation level using :func:`.create_engine`:: engine = create_engine( "mysql://scott:tiger@localhost/test", isolation_level="READ UNCOMMITTED" ) -.. versionadded:: 0.7.6 +To set using per-connection execution options:: + + connection = engine.connect() + connection = connection.execution_options( + isolation_level="READ COMMITTED" + ) + +Valid values for ``isolation_level`` include: + +* ``READ COMMITTED`` +* ``READ UNCOMMITTED`` +* ``REPEATABLE READ`` +* ``SERIALIZABLE`` +* ``AUTOCOMMIT`` + +The special ``AUTOCOMMIT`` value makes use of the various "autocommit" +attributes provided by specific DBAPIs, and is currently supported by +MySQLdb, MySQL-Client, MySQL-Connector Python, and PyMySQL. Using it, +the MySQL connection will return true for the value of +``SELECT @@autocommit;``. + +.. versionadded:: 1.1 - added support for the AUTOCOMMIT isolation level. AUTO_INCREMENT Behavior ----------------------- @@ -1439,6 +1465,8 @@ class MySQLDialect(default.DefaultDialect): level = level.replace('_', ' ') # adjust for ConnectionFairy being present + # allows attribute set e.g. "connection.autocommit = True" + # to work properly if hasattr(connection, 'connection'): connection = connection.connection diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index b16a82e043..c0a3708d4a 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -50,11 +50,12 @@ Transaction Isolation Level All Postgresql dialects support setting of transaction isolation level both via a dialect-specific parameter :paramref:`.create_engine.isolation_level` accepted by :func:`.create_engine`, -as well as the ``isolation_level`` argument as passed to +as well as the :paramref:`.Connection.execution_options.isolation_level` argument as passed to :meth:`.Connection.execution_options`. When using a non-psycopg2 dialect, this feature works by issuing the command ``SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL `` for -each new connection. +each new connection. For the special AUTOCOMMIT isolation level, DBAPI-specific +techniques are used. To set isolation level using :func:`.create_engine`:: @@ -76,10 +77,7 @@ Valid values for ``isolation_level`` include: * ``READ UNCOMMITTED`` * ``REPEATABLE READ`` * ``SERIALIZABLE`` - -The :mod:`~sqlalchemy.dialects.postgresql.psycopg2` and -:mod:`~sqlalchemy.dialects.postgresql.pg8000` dialects also offer the -special level ``AUTOCOMMIT``. +* ``AUTOCOMMIT`` - on psycopg2 / pg8000 only .. seealso:: diff --git a/test/dialect/mysql/test_dialect.py b/test/dialect/mysql/test_dialect.py index 1288b50d7b..ab719b3681 100644 --- a/test/dialect/mysql/test_dialect.py +++ b/test/dialect/mysql/test_dialect.py @@ -11,6 +11,7 @@ import datetime class DialectTest(fixtures.TestBase): __backend__ = True + __only_on__ = 'mysql' def test_ssl_arguments_mysqldb(self): from sqlalchemy.dialects.mysql import mysqldb @@ -97,6 +98,29 @@ class DialectTest(fixtures.TestBase): conn = eng.connect() eq_(conn.dialect._connection_charset, enc) + def test_autocommit_isolation_level(self): + c = testing.db.connect().execution_options( + isolation_level='AUTOCOMMIT' + ) + assert c.execute('SELECT @@autocommit;').scalar() + + c = c.execution_options(isolation_level='READ COMMITTED') + assert not c.execute('SELECT @@autocommit;').scalar() + + def test_isolation_level(self): + values = { + # sqlalchemy -> mysql + 'READ UNCOMMITTED': 'READ-UNCOMMITTED', + 'READ COMMITTED': 'READ-COMMITTED', + 'REPEATABLE READ': 'REPEATABLE-READ', + 'SERIALIZABLE': 'SERIALIZABLE' + } + for sa_value, mysql_value in values.items(): + c = testing.db.connect().execution_options( + isolation_level=sa_value + ) + assert c.execute('SELECT @@tx_isolation;').scalar() == mysql_value + class SQLModeDetectionTest(fixtures.TestBase): __only_on__ = 'mysql' __backend__ = True @@ -163,26 +187,3 @@ class ExecutionTest(fixtures.TestBase): d = testing.db.scalar(func.sysdate()) assert isinstance(d, datetime.datetime) - @testing.only_on(['mysql+mysqldb', - 'mysql+mysqlconnector', - 'mysql+pymysql', - 'mysql+cymysql']) - def test_autocommit_isolation_level(self): - c = testing.db.connect().execution_options( - isolation_level='AUTOCOMMIT' - ) - assert c.execute('SELECT @@autocommit;').scalar() - - def test_isolation_level(self): - values = { - # sqlalchemy -> mysql - 'READ UNCOMMITTED': 'READ-UNCOMMITTED', - 'READ COMMITTED': 'READ-COMMITTED', - 'REPEATABLE READ': 'REPEATABLE-READ', - 'SERIALIZABLE': 'SERIALIZABLE' - } - for sa_value, mysql_value in values.items(): - c = testing.db.connect().execution_options( - isolation_level=sa_value - ) - assert c.execute('SELECT @@tx_isolation;').scalar() == mysql_value