-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
nested_rollback
The following is just standard setup of an ORM session with a simple table mapped to the corresponding class.
Import the SQLAlchemy stuff we need:
>>> from sqlalchemy import create_engine, Column, Integer, String >>> from sqlalchemy.orm import sessionmaker >>> from sqlalchemy.ext.declarative import declarative_base
Introduce a class Developer
mapped to the table developer
:
>>> Base = declarative_base() >>> class Developer(Base): ... __tablename__ = "developer" ... id = Column(Integer, primary_key=True) ... name = Column(String(60)) ... preferred_language = Column(String(30))
Create an engine pointing to an in-memory SQLite database and setup a session:
>>> engine = create_engine("sqlite:///") >>> Base.metadata.create_all(engine) >>> Session = sessionmaker(engine) >>> session = Session()
Now it gets interesting. The following shows the behaviour of a simple database operation when used in the toplevel transaction compared to a nested transaction.
We create a new instance of the mapped class, add it to the session and make sure it actually hits the database:
>>> developer = Developer(name="Christopher", preferred_language="Python") >>> session.add(developer) >>> session.flush()
After rolling back the transaction, the instance is not attached to the session anymore and its attributes are still readable:
>>> session.rollback() >>> developer in session False >>> developer.name, developer.preferred_language ('Christopher', 'Python')
I would expect the same behaviour the instance is added in a nested transaction and rolled back with the toplevel transaction.
Let's try the same from a nested transaction:
>>> with session.begin_nested(): ... developer = Developer(name="Michael", preferred_language="Ruby") ... session.add(developer)
Now the whole thing is undone by rolling back the outer transaction. When done this way, the instance is still attached to the session and accessing its attributes raises ObjectDeletedError:
>>> session.rollback() >>> developer in session True >>> developer.name, developer.preferred_language # doctest: +ELLIPSIS Traceback (most recent call last): ... ObjectDeletedError: Instance '<Developer at 0x...>' has been deleted, or its row is otherwise not present.
I would have expected that the following code snippets are 100% equivalent wrt. the final state of the ORM session under the condition that MODIFY_DATABASE completes successfully and that the session is initially clean:
Without nested transaction:
MODIFY_DATABASE(session) session.flush() session.rollback()
With nested transaction:
with session.begin_nested(): MODIFY_DATABASE(session) session.rollback()