Opened 3 hours ago
Last modified 3 hours ago
#37406 new Uncategorized
BaseDatabaseSchemaEditor.__exit__ doesn't exit its atomic block when deferred SQL raises — at Version 1
| Reported by: | Gavin Wahl(vendor) | Owned by: | |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 6.1 |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
BaseDatabaseSchemaEditor (normally accessed through connection.schema_editor) implements the context manager protocol and wraps transaction.atomic() in its __enter__ and __exit__. In __exit__, it runs the deferred SQL and then calls self.atomic.__exit__. If the deferred SQL raises, the second call never happens and the atomic block stays open.
After that the connection is stuck. in_atomic_block is True with no atomic block on the stack, so nothing will ever commit. Once anything sets needs_rollback you get TransactionManagementError on every query. close() doesn't help because inside an atomic block it keeps the dead connection object around and waits for an Atomic.__exit__ to reset it.
This doesn't affect manage.py migrate from a shell, since the process exits and the server rolls back. It matters if you catch the error and keep going, e.g. tests, call_command("migrate"), or a tool using schema_editor directly.
There is an existing test_migrations_not_applied_on_deferred_sql_failure but it has atomic = False so it doesn't hit this.
Minimal reproducer attached.
Related #29721
Change History (2)
by , 3 hours ago
| Attachment: | deferred_fail_atomic.py added |
|---|
comment:1 by , 3 hours ago
| Description: | modified (diff) |
|---|