]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Document workaround for aiosqlite savepoints
authorVlastimil Zíma <vlastimil.zima@nic.cz>
Fri, 20 Oct 2023 17:58:21 +0000 (13:58 -0400)
committermike bayer <mike_mp@zzzcomputing.com>
Tue, 24 Oct 2023 14:51:29 +0000 (14:51 +0000)
### Description
Update docs with workaround for SAVEPOINT in aiosqlite, based on https://github.com/sqlalchemy/sqlalchemy/discussions/10463

### Checklist
This pull request is:

- [x] A documentation / typographical / small typing error fix
- Good to go, no issue or tests are needed

Closes: #10491
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/10491
Pull-request-sha: 0516c205d2c4c046d449b5684c13a06f813dabe1

Change-Id: I6bb92f18c99f2a53f5488a2d5812781cb2a003b7

lib/sqlalchemy/dialects/sqlite/aiosqlite.py
lib/sqlalchemy/dialects/sqlite/base.py
lib/sqlalchemy/ext/asyncio/scoping.py
lib/sqlalchemy/ext/asyncio/session.py
lib/sqlalchemy/orm/scoping.py
lib/sqlalchemy/orm/session.py

index bfb2805c77d2bbc0f216a769a33ecb8eed3eee97..d9438d1880e3a58d367e5f8bd5db6640ea21a42c 100644 (file)
@@ -44,6 +44,36 @@ User-Defined Functions
 aiosqlite extends pysqlite to support async, so we can create our own user-defined functions (UDFs)
 in Python and use them directly in SQLite queries as described here: :ref:`pysqlite_udfs`.
 
+.. _aiosqlite_serializable:
+
+Serializable isolation / Savepoints / Transactional DDL (asyncio version)
+-------------------------------------------------------------------------
+
+Similarly to pysqlite, aiosqlite does not support SAVEPOINT feature.
+
+The solution is similar to :ref:`pysqlite_serializable`. This is achieved by the event listeners in async::
+
+    from sqlalchemy import create_engine, event
+    from sqlalchemy.ext.asyncio import create_async_engine
+
+    engine = create_async_engine("sqlite+aiosqlite:///myfile.db")
+
+    @event.listens_for(engine.sync_engine, "connect")
+    def do_connect(dbapi_connection, connection_record):
+        # disable aiosqlite's emitting of the BEGIN statement entirely.
+        # also stops it from emitting COMMIT before any DDL.
+        dbapi_connection.isolation_level = None
+
+    @event.listens_for(engine.sync_engine, "begin")
+    def do_begin(conn):
+        # emit our own BEGIN
+        conn.exec_driver_sql("BEGIN")
+
+.. warning:: When using the above recipe, it is advised to not use the
+   :paramref:`.Connection.execution_options.isolation_level` setting on
+   :class:`_engine.Connection` and :func:`_sa.create_engine`
+   with the SQLite driver,
+   as this function necessarily will also alter the ".isolation_level" setting.
 
 """  # noqa
 
index 4bce3a3d29c310b7071ca54a1329a8ba542d93ff..d4eb3bca41b36203de6de0bbb022d89e46e47a97 100644 (file)
@@ -215,7 +215,7 @@ by *not even emitting BEGIN* until the first write operation.
     SQLite's transactional scope is impacted by unresolved
     issues in the pysqlite driver, which defers BEGIN statements to a greater
     degree than is often feasible. See the section :ref:`pysqlite_serializable`
-    for techniques to work around this behavior.
+    or :ref:`aiosqlite_serializable` for techniques to work around this behavior.
 
 .. seealso::
 
@@ -273,8 +273,9 @@ won't work at all with pysqlite unless workarounds are taken.
 .. warning::
 
     SQLite's SAVEPOINT feature is impacted by unresolved
-    issues in the pysqlite driver, which defers BEGIN statements to a greater
-    degree than is often feasible. See the section :ref:`pysqlite_serializable`
+    issues in the pysqlite and aiosqlite drivers, which defer BEGIN statements
+    to a greater degree than is often feasible. See the sections
+    :ref:`pysqlite_serializable` and :ref:`aiosqlite_serializable`
     for techniques to work around this behavior.
 
 Transactional DDL
index 7ac11ffacee48756209bbc4aad0365c012f2400e..4c68f53ffa8959086b5d6e8c8c1b94e2bc9209fa 100644 (file)
@@ -394,6 +394,12 @@ class async_scoped_session(Generic[_AS]):
         For a general description of ORM begin nested, see
         :meth:`_orm.Session.begin_nested`.
 
+        .. seealso::
+
+            :ref:`aiosqlite_serializable` - special workarounds required
+            with the SQLite asyncio driver in order for SAVEPOINT to work
+            correctly.
+
 
         """  # noqa: E501
 
index b4b5ab3fbecb459c615777713678d1d68f438815..30232e59cbbd1276fd572e3dd6ceb87df51e72ee 100644 (file)
@@ -980,6 +980,12 @@ class AsyncSession(ReversibleProxy[Session]):
         For a general description of ORM begin nested, see
         :meth:`_orm.Session.begin_nested`.
 
+        .. seealso::
+
+            :ref:`aiosqlite_serializable` - special workarounds required
+            with the SQLite asyncio driver in order for SAVEPOINT to work
+            correctly.
+
         """
 
         return AsyncSessionTransaction(self, nested=True)
index e8dfb8a80a5d36f7c7617c08bcdf11c2354f10bd..ab632bdd5644ddb18be0ee396dd3d634b78dfd40 100644 (file)
@@ -468,7 +468,8 @@ class scoped_session(Generic[_S]):
 
             :ref:`pysqlite_serializable` - special workarounds required
             with the SQLite driver in order for SAVEPOINT to work
-            correctly.
+            correctly. For asyncio use cases, see the section
+            :ref:`aiosqlite_serializable`.
 
 
         """  # noqa: E501
index e247e0d143fd2869604e09c9d00dafc1d53ff40e..d8619812719e998e564d14b40d27fe245dc6d426 100644 (file)
@@ -1632,8 +1632,9 @@ class Session(_SessionClassMethods, EventTarget):
 
             .. tip:: When using SQLite, the SQLite driver included through
                Python 3.11 does not handle SAVEPOINTs correctly in all cases
-               without workarounds. See the section
-               :ref:`pysqlite_serializable` for details on current workarounds.
+               without workarounds. See the sections
+               :ref:`pysqlite_serializable` and :ref:`aiosqlite_serializable`
+               for details on current workarounds.
 
           * ``"control_fully"`` - the :class:`_orm.Session` will take
             control of the given transaction as its own;
@@ -1902,7 +1903,8 @@ class Session(_SessionClassMethods, EventTarget):
 
             :ref:`pysqlite_serializable` - special workarounds required
             with the SQLite driver in order for SAVEPOINT to work
-            correctly.
+            correctly. For asyncio use cases, see the section
+            :ref:`aiosqlite_serializable`.
 
         """
         return self.begin(nested=True)