.. autoattribute:: autocommit
The property is writable for sync connections, read-only for async
- ones: you should call ``await`` `~AsyncConnection.set_autocommit`\
+ ones: you should call ``await`` `~AsyncConnection.set_autocommit`
:samp:`({value})` instead.
+ The following three properties control the characteristics of new
+ transactions. See :ref:`transaction-characteristics` for detils.
+
+ .. autoattribute:: isolation_level
+
+ `!None` means use the default set in the default_transaction_isolation__
+ configuration parameter of the server.
+
+ .. __: https://www.postgresql.org/docs/current/runtime-config-client.html
+ #GUC-DEFAULT-TRANSACTION-ISOLATION
+
+ .. autoattribute:: read_only
+
+ `!None` means use the default set in the default_transaction_read_only__
+ configuration parameter of the server.
+
+ .. __: https://www.postgresql.org/docs/current/runtime-config-client.html
+ #GUC-DEFAULT-TRANSACTION-READ-ONLY
+
+ .. autoattribute:: deferrable
+
+ `!None` means use the default set in the default_transaction_deferrable__
+ configuration parameter of the server.
+
+ .. __: https://www.postgresql.org/docs/current/runtime-config-client.html
+ #GUC-DEFAULT-TRANSACTION-DEFERRABLE
+
+
.. rubric:: Checking and configuring the connection state
.. autoattribute:: client_encoding
.. automethod:: notifies
.. automethod:: set_client_encoding
.. automethod:: set_autocommit
+ .. automethod:: set_isolation_level
+ .. automethod:: set_read_only
+ .. automethod:: set_deferrable
Connection support objects
.. rubric:: Objects involved in :ref:`transactions`
+.. autoclass:: IsolationLevel
+ :members:
+
+ The value is usually used with the `Connection.isolation_level` property.
+
+ Check the PostgreSQL documentation for a description of the effects of the
+ different `levels of transaction isolation`__.
+
+ .. __: https://www.postgresql.org/docs/current/transaction-iso.html
+
.. autoclass:: Transaction()
.. autoattribute:: savepoint_name
*InFailedSqlTransaction: current transaction is aborted, commands ignored
until end of transaction block*, it means that **a previous operation
failed** and the database session is in a state of error. You need to call
- `!rollback()` if you want to keep on using the same connection.
+ `~Connection.rollback()` if you want to keep on using the same connection.
.. _autocommit:
# If `Rollback` is raised, it would propagate only up to this block,
# and the program would continue from here with no exception.
+
+
+.. _transaction-characteristics:
+
+Transaction characteristics
+---------------------------
+
+You can set `transaction parameters`__ for the transactions that Psycopg
+handles. They affect the transactions started implicitly by non-autocommit
+transactions and the ones started explicitly by `Connection.transaction()` for
+both autocommit and non-autocommit transactions. Leaving these parameters to
+`!None` will leave the behaviour to the server's default (which is controlled
+by server settings such as default_transaction_isolation__).
+
+.. __: https://www.postgresql.org/docs/current/sql-set-transaction.html
+.. __: https://www.postgresql.org/docs/current/runtime-config-client.html
+ #GUC-DEFAULT-TRANSACTION-ISOLATION
+
+In order to set these parameters you can use the connection attributes
+`~Connection.isolation_level`, `~Connection.read_only`,
+`~Connection.deferrable`. For async connections you must use the equivalent
+`~AsyncConnection.set_isolation_level()` method and similar. The parameters
+can only be changed if there isn't a transaction already active on the
+connection.
+
+.. warning::
+
+ Applications running at `~IsolationLevel.REPEATABLE_READ` or
+ `~IsolationLevel.SERIALIZABLE` isolation level are exposed to serialization
+ failures. `In certain concurrent update cases`__, PostgreSQL will raise an
+ exception looking like::
+
+ psycopg2.errors.SerializationFailure: could not serialize access
+ due to concurrent update
+
+ In this case the application must be prepared to repeat the operation that
+ caused the exception.
+
+ .. __: https://www.postgresql.org/docs/current/transaction-iso.html
+ #XACT-REPEATABLE-READ
from .abc import ConnectionType, Params, PQGen, PQGenConn, Query, RV
from .sql import Composable
from .rows import Row, RowFactory, tuple_row, TupleRow
+from ._enums import IsolationLevel
from .compat import asynccontextmanager
from .cursor import Cursor, AsyncCursor
from ._cmodule import _psycopg
# Time after which the connection should be closed
self._expire_at: float
+ self._isolation_level: Optional[IsolationLevel] = None
+ self._read_only: Optional[bool] = None
+ self._deferrable: Optional[bool] = None
+
def __del__(self) -> None:
# If fails on connection we might not have this attribute yet
if not hasattr(self, "pgconn"):
self._set_autocommit(value)
def _set_autocommit(self, value: bool) -> None:
- # Base implementation, not thread safe
- # subclasses must call it holding a lock
+ # Base implementation, not thread safe.
+ # Subclasses must call it holding a lock
+ self._check_intrans("autocommit")
+ self._autocommit = value
+
+ @property
+ def isolation_level(self) -> Optional[IsolationLevel]:
+ """
+ The isolation level of the new transactions started on the connection.
+ """
+ return self._isolation_level
+
+ @isolation_level.setter
+ def isolation_level(self, value: Optional[IsolationLevel]) -> None:
+ self._set_isolation_level(value)
+
+ def _set_isolation_level(self, value: Optional[IsolationLevel]) -> None:
+ # Base implementation, not thread safe.
+ # Subclasses must call it holding a lock
+ self._check_intrans("isolation_level")
+ self._isolation_level = (
+ IsolationLevel(value) if value is not None else None
+ )
+
+ @property
+ def read_only(self) -> Optional[bool]:
+ """
+ The read-only state of the new transactions started on the connection.
+ """
+ return self._read_only
+
+ @read_only.setter
+ def read_only(self, value: Optional[bool]) -> None:
+ self._set_read_only(value)
+
+ def _set_read_only(self, value: Optional[bool]) -> None:
+ # Base implementation, not thread safe.
+ # Subclasses must call it holding a lock
+ self._check_intrans("read_only")
+ self._read_only = value
+
+ @property
+ def deferrable(self) -> Optional[bool]:
+ """
+ The deferrable state of the new transactions started on the connection.
+ """
+ return self._deferrable
+
+ @deferrable.setter
+ def deferrable(self, value: Optional[bool]) -> None:
+ self._set_deferrable(value)
+
+ def _set_deferrable(self, value: Optional[bool]) -> None:
+ # Base implementation, not thread safe.
+ # Subclasses must call it holding a lock
+ self._check_intrans("deferrable")
+ self._deferrable = value
+
+ def _check_intrans(self, attribute: str) -> None:
+ # Raise an exception if we are in a transaction
status = self.pgconn.transaction_status
if status != TransactionStatus.IDLE:
if self._savepoints:
raise e.ProgrammingError(
- "couldn't change autocommit state: "
+ f"can't change {attribute!r} now: "
"connection.transaction() context in progress"
)
else:
raise e.ProgrammingError(
- "couldn't change autocommit state: "
+ f"can't change {attribute!r} now: "
"connection in transaction status "
f"{TransactionStatus(status).name}"
)
- self._autocommit = value
-
@property
def client_encoding(self) -> str:
"""The Python codec name of the connection's client encoding."""
with self.lock:
super()._set_autocommit(value)
+ def _set_isolation_level(self, value: Optional[IsolationLevel]) -> None:
+ with self.lock:
+ super()._set_isolation_level(value)
+
+ def _set_read_only(self, value: Optional[bool]) -> None:
+ with self.lock:
+ super()._set_read_only(value)
+
+ def _set_deferrable(self, value: Optional[bool]) -> None:
+ with self.lock:
+ super()._set_deferrable(value)
+
def _set_client_encoding(self, name: str) -> None:
with self.lock:
self.wait(self._set_client_encoding_gen(name))
) -> RV:
return await waiting.wait_conn_async(gen, timeout)
+ def _set_autocommit(self, value: bool) -> None:
+ self._no_set_async("autocommit")
+
+ async def set_autocommit(self, value: bool) -> None:
+ """Async version of the `~Connection.autocommit` setter."""
+ async with self.lock:
+ super()._set_autocommit(value)
+
+ def _set_isolation_level(self, value: Optional[IsolationLevel]) -> None:
+ self._no_set_async("isolation_level")
+
+ async def set_isolation_level(
+ self, value: Optional[IsolationLevel]
+ ) -> None:
+ """Async version of the `~Connection.isolation_level` setter."""
+ async with self.lock:
+ super()._set_isolation_level(value)
+
+ def _set_read_only(self, value: Optional[bool]) -> None:
+ self._no_set_async("read_only")
+
+ async def set_read_only(self, value: Optional[bool]) -> None:
+ """Async version of the `~Connection.read_only` setter."""
+ async with self.lock:
+ super()._set_read_only(value)
+
+ def _set_deferrable(self, value: Optional[bool]) -> None:
+ self._no_set_async("deferrable")
+
+ async def set_deferrable(self, value: Optional[bool]) -> None:
+ """Async version of the `~Connection.deferrable` setter."""
+ async with self.lock:
+ super()._set_deferrable(value)
+
def _set_client_encoding(self, name: str) -> None:
- raise AttributeError(
- "'client_encoding' is read-only on async connections:"
- " please use await .set_client_encoding() instead."
- )
+ self._no_set_async("client_encoding")
async def set_client_encoding(self, name: str) -> None:
"""Async version of the `~Connection.client_encoding` setter."""
async with self.lock:
await self.wait(self._set_client_encoding_gen(name))
- def _set_autocommit(self, value: bool) -> None:
+ def _no_set_async(self, attribute: str) -> None:
raise AttributeError(
- "autocommit is read-only on async connections:"
- " please use await connection.set_autocommit() instead."
- " Note that you can pass an 'autocommit' value to 'connect()'"
- " if it doesn't need to change during the connection's lifetime."
+ f"'the {attribute!r} property is read-only on async connections:"
+ f" please use 'await .set_{attribute}()' instead."
)
-
- async def set_autocommit(self, value: bool) -> None:
- """Async version of the `~Connection.autocommit` setter."""
- async with self.lock:
- super()._set_autocommit(value)