From 8403c7083d9a1acbb9410ec0fd92331eace5ca8b Mon Sep 17 00:00:00 2001 From: Murod Date: Sat, 27 Jun 2026 09:38:42 +0900 Subject: [PATCH] Don't emit DROP of version table in offline mode at base When running ``stamp`` or ``downgrade`` to ``base`` in offline ("--sql") mode, Alembic emitted a ``DROP TABLE alembic_version`` statement, whereas the same operations in online mode never drop the version table. This offline-only DROP, added when the version table was first introduced, was an inconsistency. Offline mode now matches online behavior and no longer drops the version table; the table is still created when it does not exist. Fixes: #1822 --- alembic/runtime/migration.py | 8 +++++--- docs/build/unreleased/1822.rst | 10 ++++++++++ tests/test_command.py | 11 ++++++++++- 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 docs/build/unreleased/1822.rst diff --git a/alembic/runtime/migration.py b/alembic/runtime/migration.py index 3fccf22a6..9ef12211c 100644 --- a/alembic/runtime/migration.py +++ b/alembic/runtime/migration.py @@ -639,9 +639,11 @@ def run_migrations(self, **kw: Any) -> None: run_args=kw, ) - if self.as_sql and not head_maintainer.heads: - assert self.connection is not None - self._version.drop(self.connection) + # NOTE: offline ("--sql") mode intentionally does not emit a DROP + # of the version table when ending at base. Online mode never drops + # the version table (e.g. ``downgrade base`` only deletes its row), + # so dropping it in offline mode only was an inconsistency present + # since the version table was first introduced. See #1822. def _in_connection_transaction(self) -> bool: try: diff --git a/docs/build/unreleased/1822.rst b/docs/build/unreleased/1822.rst new file mode 100644 index 000000000..edf343697 --- /dev/null +++ b/docs/build/unreleased/1822.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, commands + :tickets: 1822 + + Fixed inconsistency where running ``stamp`` or ``downgrade`` to ``base`` in + offline (``--sql``) mode would emit a ``DROP TABLE alembic_version`` + statement, while the same operations in online mode never drop the version + table. Offline mode no longer emits this ``DROP``, matching online + behavior. The version table continues to be created when it does not exist; + only the spurious offline-only drop has been removed. diff --git a/tests/test_command.py b/tests/test_command.py index 391a19d00..9cd49f085 100644 --- a/tests/test_command.py +++ b/tests/test_command.py @@ -988,11 +988,20 @@ def test_version_to_none(self): command.downgrade(self.cfg, "%s:base" % self.c, sql=True) assert "CREATE TABLE alembic_version" not in buf.getvalue() assert "INSERT INTO alembic_version" not in buf.getvalue() - assert "DROP TABLE alembic_version" in buf.getvalue() + # offline mode no longer drops the version table, matching online + # mode which never drops it. See #1822. + assert "DROP TABLE alembic_version" not in buf.getvalue() assert "DROP STEP 3" in buf.getvalue() assert "DROP STEP 2" in buf.getvalue() assert "DROP STEP 1" in buf.getvalue() + def test_sql_stamp_to_base_no_drop(self): + # stamping to "base" in offline mode must not emit a DROP of the + # version table; online mode never drops it. See #1822. + with capture_context_buffer() as buf: + command.stamp(self.cfg, "base", sql=True) + assert "DROP TABLE alembic_version" not in buf.getvalue() + def test_version_to_middle(self): with capture_context_buffer() as buf: command.downgrade(self.cfg, "%s:%s" % (self.c, self.a), sql=True)