From: Mike Bayer Date: Tue, 8 Sep 2020 15:01:28 +0000 (-0400) Subject: PostgreSQL dialect-level isolation_level parameter is legacy X-Git-Tag: rel_1_3_20~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=39f560a7d952f5de7dc88707fdcb3dc9a163f8bb;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git PostgreSQL dialect-level isolation_level parameter is legacy The isolation level section in the docs inadvertently copied the PostgreSQL example using the PGDialect.isolation_level parameter and not the execution_options. ensure only the execution_options version is documented. Change-Id: I94e02ede62d3dded40e3fcbce8d04608dc063108 (cherry picked from commit 6dc8d1dc6955db8107b683f2c2f3e4b62aad574b) --- diff --git a/doc/build/core/connections.rst b/doc/build/core/connections.rst index 74f2b13a3a..28d69a176b 100644 --- a/doc/build/core/connections.rst +++ b/doc/build/core/connections.rst @@ -382,10 +382,10 @@ Instead, each statement invoked upon the connection will commit any changes automatically; it sometimes also means that the connection itself will use fewer server-side database resources. For this reason and others, "autocommit" mode is often desirable for non-transactional applications that need to read -individual tables or rows in isolation of a true ACID transaction. +individual tables or rows outside the scope of a true ACID transaction. -SQLAlchemy dialects can support these isolation levels as well as autocommit to -as great a degree as possible. The levels are set via family of +SQLAlchemy dialects should support these isolation levels as well as autocommit +to as great a degree as possible. The levels are set via family of "execution_options" parameters and methods that are throughout the Core, such as the :meth:`_engine.Connection.execution_options` method. The parameter is known as :paramref:`_engine.Connection.execution_options.isolation_level` and @@ -402,30 +402,39 @@ the values are strings which are typically a subset of the following names:: Not every DBAPI supports every value; if an unsupported value is used for a certain backend, an error is raised. -For example, to force REPEATABLE READ on a specific connection:: +For example, to force REPEATABLE READ on a specific connection, then +begin a transaction:: with engine.connect().execution_options(isolation_level="REPEATABLE READ") as connection: - connection.execute() + with connection.begin(): + connection.execute() -The :paramref:`_engine.Connection.execution_options.isolation_level` option -may also be set engine wide, as is often preferable. It can be set either -within :func:`_sa.create_engine` directly via the :paramref:`_sa.create_engine.execution_options` -parameter:: +The :paramref:`_engine.Connection.execution_options.isolation_level` option may +also be set engine wide, as is often preferable. This is achieved by +passing it within the :paramref:`_sa.create_engine.execution_options` +parameter to :func:`_sa.create_engine`:: from sqlalchemy import create_engine eng = create_engine( "postgresql://scott:tiger@localhost/test", - isolation_level='REPEATABLE READ' + execution_options={ + "isolation_level": "REPEATABLE READ" + } ) -Or for an application that chooses between multiple levels, as may be the case -for the use of "AUTOCOMMIT" to switch between "transactional" and "read-only" -engines, the :meth:`_engine.Engine.execution_options` method will provide a shallow -copy of the :class:`_engine.Engine` that will apply the given isolation -level to all connections:: +With the above setting, the DBAPI connection will be set to use a +``"REPEATABLE READ"`` isolation level setting for each new transaction +begun. +An application that frequently chooses to run operations within different +isolation levels may wish to create multiple "sub-engines" of a lead +:class:`_engine.Engine`, each of which will be configured to a different +isolation level. One such use case is an application that has operations +that break into "transactional" and "read-only" operations, a separate +:class:`_engine.Engine` that makes use of ``"AUTOCOMMIT"`` may be +separated off from the main engine:: from sqlalchemy import create_engine @@ -434,9 +443,11 @@ level to all connections:: autocommit_engine = eng.execution_options(isolation_level="AUTOCOMMIT") -Above, both ``eng`` and ``autocommit_engine`` share the same dialect -and connection pool. However the "AUTOCOMMIT" mode will be set upon connections -when they are acquired from the ``autocommit_engine``. +Above, the :meth:`_engine.Engine.execution_options` method creates a shallow +copy of the original :class:`_engine.Engine`. Both ``eng`` and +``autocommit_engine`` share the same dialect and connection pool. However, the +"AUTOCOMMIT" mode will be set upon connections when they are acquired from the +``autocommit_engine``. The isolation level setting, regardless of which one it is, is unconditionally reverted when a connection is returned to the connection pool. diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 86a857f138..2c68a5969b 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -82,38 +82,46 @@ Will generate on the backing database as:: Transaction Isolation Level --------------------------- -All PostgreSQL dialects support setting of transaction isolation level -both via a dialect-specific parameter -:paramref:`_sa.create_engine.isolation_level` accepted by -:func:`_sa.create_engine`, -as well as the :paramref:`.Connection.execution_options.isolation_level` -argument as passed to :meth:`_engine.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. For the special AUTOCOMMIT isolation level, -DBAPI-specific techniques are used. +Most SQLAlchemy dialects support setting of transaction isolation level +using the :paramref:`_sa.create_engine.execution_options` parameter +at the :func:`_sa.create_engine` level, and at the :class:`_engine.Connection` +level via the :paramref:`.Connection.execution_options.isolation_level` +parameter. + +For PostgreSQL dialects, this feature works either by making use of the +DBAPI-specific features, such as psycopg2's isolation level flags which will +embed the isolation level setting inline with the ``"BEGIN"`` statement, or for +DBAPIs with no direct support by emitting ``SET SESSION CHARACTERISTICS AS +TRANSACTION ISOLATION LEVEL `` ahead of the ``"BEGIN"`` statement +emitted by the DBAPI. For the special AUTOCOMMIT isolation level, +DBAPI-specific techniques are used which is typically an ``.autocommit`` +flag on the DBAPI connection object. To set isolation level using :func:`_sa.create_engine`:: engine = create_engine( "postgresql+pg8000://scott:tiger@localhost/test", - isolation_level="READ UNCOMMITTED" + execution_options={ + "isolation_level": "REPEATABLE READ" + } ) To set using per-connection execution options:: - connection = engine.connect() - connection = connection.execution_options( - isolation_level="READ COMMITTED" - ) + with engine.connect() as conn: + conn = conn.execution_options( + isolation_level="REPEATABLE READ" + ) + with conn.begin(): + # ... work with transaction -Valid values for ``isolation_level`` include: +Valid values for ``isolation_level`` on most PostgreSQL dialects include: * ``READ COMMITTED`` * ``READ UNCOMMITTED`` * ``REPEATABLE READ`` * ``SERIALIZABLE`` -* ``AUTOCOMMIT`` - on psycopg2 / pg8000 only +* ``AUTOCOMMIT`` .. seealso:: @@ -2591,6 +2599,10 @@ class PGDialect(default.DefaultDialect): **kwargs ): default.DefaultDialect.__init__(self, **kwargs) + + # the isolation_level parameter to the PGDialect itself is legacy. + # still works however the execution_options method is the one that + # is documented. self.isolation_level = isolation_level self._json_deserializer = json_deserializer self._json_serializer = json_serializer